diff --git a/.github/workflows/build-nym-vpn-core-android.yml b/.github/workflows/build-nym-vpn-core-android.yml index ba266a34ab..bd51424dc8 100644 --- a/.github/workflows/build-nym-vpn-core-android.yml +++ b/.github/workflows/build-nym-vpn-core-android.yml @@ -9,7 +9,7 @@ on: env: CARGO_TERM_COLOR: always CARGO_TARGET: release - WG_GO_LIB_PATH: ${{ github.workspace }}/lib + WG_GO_LIB_PATH: ${{ github.workspace }}/build/lib/aarch64-linux-android WG_GO_LIB_NAME: wireguard-go_aarch64-linux-android UPLOAD_DIR_ANDROID: android_artifacts diff --git a/.github/workflows/build-nym-vpn-core-ios.yml b/.github/workflows/build-nym-vpn-core-ios.yml index d3b9f8fe3a..9d5d241de9 100644 --- a/.github/workflows/build-nym-vpn-core-ios.yml +++ b/.github/workflows/build-nym-vpn-core-ios.yml @@ -7,10 +7,11 @@ on: value: ${{ jobs.build-ios.outputs.RUST_VERSION }} env: + IPHONEOS_DEPLOYMENT_TARGET: 16.0 CARGO_TERM_COLOR: always CARGO_TARGET: release - WG_GO_LIB_PATH: ${{ github.workspace }}/lib - WG_GO_LIB_NAME: wireguard-go_apple_universal + WG_GO_LIB_PATH: ${{ github.workspace }}/build/lib + WG_GO_LIB_NAME: wireguard-go_ios_universal UPLOAD_DIR_IOS: ios_artifacts jobs: @@ -61,7 +62,7 @@ jobs: RUSTFLAGS: "-L ${{ env.WG_GO_LIB_PATH }}" run: | cargo swift package -p ios -n NymVpnLib --${{ env.CARGO_TARGET }} - ls -la ../target/release/ || true + ls -la ../target/universal-ios/release/ || true - name: Get rust version used for build id: rust-version diff --git a/.github/workflows/build-nym-vpn-core-mac.yml b/.github/workflows/build-nym-vpn-core-mac.yml index 437786bdab..4ce7721e4e 100644 --- a/.github/workflows/build-nym-vpn-core-mac.yml +++ b/.github/workflows/build-nym-vpn-core-mac.yml @@ -4,7 +4,7 @@ on: [workflow_dispatch, workflow_call] env: CARGO_TERM_COLOR: always CARGO_TARGET: release - WG_GO_LIB_PATH: ${{ github.workspace }}/lib + WG_GO_LIB_PATH: ${{ github.workspace }}/build/lib/universal-apple-darwin WG_GO_LIB_NAME: wireguard-go_apple_universal UPLOAD_DIR_MAC: mac_artifacts diff --git a/.github/workflows/build-nym-vpn-core-windows.yml b/.github/workflows/build-nym-vpn-core-windows.yml index 9884374afa..075616793d 100644 --- a/.github/workflows/build-nym-vpn-core-windows.yml +++ b/.github/workflows/build-nym-vpn-core-windows.yml @@ -11,7 +11,7 @@ on: env: CARGO_TERM_COLOR: always CARGO_TARGET: release - LIBS_PATH: '${{ github.workspace }}/lib' + LIBS_PATH: '${{ github.workspace }}/build/lib/x86_64-pc-windows-msvc' WG_GO_LIB_NAME: wireguard-go_x86_64-pc-windows-msvc MULLVAD_LIB_NAME: winfw UPLOAD_DIR_WINDOWS: windows_artifacts diff --git a/.github/workflows/build-wireguard-go-ios.yml b/.github/workflows/build-wireguard-go-ios.yml index 4a06def6ac..60ae6886d3 100644 --- a/.github/workflows/build-wireguard-go-ios.yml +++ b/.github/workflows/build-wireguard-go-ios.yml @@ -25,5 +25,7 @@ jobs: with: name: wireguard-go_ios_universal path: | - build/lib/universal-apple-darwin + build/lib/aarch64-apple-ios + build/lib/aarch64-apple-ios-sim + build/lib/x86_64-apple-ios retention-days: 10 diff --git a/.github/workflows/ci-nym-vpn-core.yml b/.github/workflows/ci-nym-vpn-core.yml index 0e17177f36..986767f5fc 100644 --- a/.github/workflows/ci-nym-vpn-core.yml +++ b/.github/workflows/ci-nym-vpn-core.yml @@ -202,7 +202,8 @@ jobs: if: contains(matrix.os, 'macos') || contains(matrix.os, 'mac-m1') working-directory: nym-vpn-core run: | - cargo build --verbose --target aarch64-apple-ios -p nym-vpn-lib + rustflags="-L ${GITHUB_WORKSPACE}/build/lib/aarch64-apple-ios" + RUSTFLAGS=$rustflags cargo build --verbose --target aarch64-apple-ios -p nym-vpn-lib - name: Generate uniffi (Android) if: contains(matrix.target, 'android') diff --git a/nym-vpn-apple/.swiftlint.yml b/nym-vpn-apple/.swiftlint.yml index 1dedf7159b..fdedd2551c 100644 --- a/nym-vpn-apple/.swiftlint.yml +++ b/nym-vpn-apple/.swiftlint.yml @@ -119,7 +119,7 @@ line_length: error: 240 multiline_parameters: - allowsSingleLine: false + allows_single_line: true nesting: type_level: 3 diff --git a/nym-vpn-apple/MixnetLibrary/Package.swift b/nym-vpn-apple/MixnetLibrary/Package.swift index 5f24ec7d80..dc24275652 100644 --- a/nym-vpn-apple/MixnetLibrary/Package.swift +++ b/nym-vpn-apple/MixnetLibrary/Package.swift @@ -23,8 +23,8 @@ let package = Package( ), .binaryTarget( name: "NymVpnLib", - url: "https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-core-v0.1.16/nym-vpn-core-v0.1.16_ios_universal.zip", - checksum: "41a5ecb8b0ab7cc2e6130179863761d12bcc988eda6c269cb00bd01a43449741" + url: "https://github.com/nymtech/nym-vpn-client/releases/download/nym-vpn-core-nightly/nym-vpn-core-v0.1.17-dev_ios_universal.zip", + checksum: "66a830cd46b912df1c11704b487f7a661299ff4583d142770ae04c0ceeaaad5c" ), .testTarget( name: "MixnetLibraryTests", diff --git a/nym-vpn-apple/MixnetLibrary/Sources/MixnetLibrary/nym_vpn_lib.swift b/nym-vpn-apple/MixnetLibrary/Sources/MixnetLibrary/nym_vpn_lib.swift index 344de49c00..07f2ad7c42 100644 --- a/nym-vpn-apple/MixnetLibrary/Sources/MixnetLibrary/nym_vpn_lib.swift +++ b/nym-vpn-apple/MixnetLibrary/Sources/MixnetLibrary/nym_vpn_lib.swift @@ -20,15 +20,15 @@ fileprivate extension RustBuffer { } self.init(capacity: rbuf.capacity, len: rbuf.len, data: rbuf.data) } - + static func empty() -> RustBuffer { RustBuffer(capacity: 0, len:0, data: nil) } - + static func from(_ ptr: UnsafeBufferPointer) -> RustBuffer { try! rustCall { ffi_nym_vpn_lib_rustbuffer_from_bytes(ForeignBytes(bufferPointer: ptr), $0) } } - + // Frees the buffer in place. // The buffer must not be used after this is called. func deallocate() { @@ -154,11 +154,11 @@ fileprivate func writeDouble(_ writer: inout [UInt8], _ value: Double) { } // Protocol for types that transfer other types across the FFI. This is -// analogous go the Rust trait of the same name. +// analogous to the Rust trait of the same name. fileprivate protocol FfiConverter { associatedtype FfiType associatedtype SwiftType - + static func lift(_ value: FfiType) throws -> SwiftType static func lower(_ value: SwiftType) -> FfiType static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType @@ -172,7 +172,7 @@ extension FfiConverterPrimitive { public static func lift(_ value: FfiType) throws -> SwiftType { return value } - + public static func lower(_ value: SwiftType) -> FfiType { return value } @@ -192,11 +192,11 @@ extension FfiConverterRustBuffer { buf.deallocate() return value } - + public static func lower(_ value: SwiftType) -> RustBuffer { - var writer = createWriter() - write(value, into: &writer) - return RustBuffer(bytes: writer) + var writer = createWriter() + write(value, into: &writer) + return RustBuffer(bytes: writer) } } // An error type for FFI errors. These errors occur at the UniFFI level, not @@ -211,7 +211,7 @@ fileprivate enum UniffiInternalError: LocalizedError { case unexpectedRustCallError case unexpectedStaleHandle case rustPanic(_ message: String) - + public var errorDescription: String? { switch self { case .bufferOverflow: return "Reading the requested value would read past the end of the buffer" @@ -254,18 +254,19 @@ fileprivate extension RustCallStatus { } private func rustCall(_ callback: (UnsafeMutablePointer) -> T) throws -> T { - try makeRustCall(callback, errorHandler: nil) + let neverThrow: ((RustBuffer) throws -> Never)? = nil + return try makeRustCall(callback, errorHandler: neverThrow) } -private func rustCallWithError( - _ errorHandler: @escaping (RustBuffer) throws -> Error, +private func rustCallWithError( + _ errorHandler: @escaping (RustBuffer) throws -> E, _ callback: (UnsafeMutablePointer) -> T) throws -> T { - try makeRustCall(callback, errorHandler: errorHandler) - } + try makeRustCall(callback, errorHandler: errorHandler) +} -private func makeRustCall( +private func makeRustCall( _ callback: (UnsafeMutablePointer) -> T, - errorHandler: ((RustBuffer) throws -> Error)? + errorHandler: ((RustBuffer) throws -> E)? ) throws -> T { uniffiEnsureInitialized() var callStatus = RustCallStatus.init() @@ -274,38 +275,38 @@ private func makeRustCall( return returnedVal } -private func uniffiCheckCallStatus( +private func uniffiCheckCallStatus( callStatus: RustCallStatus, - errorHandler: ((RustBuffer) throws -> Error)? + errorHandler: ((RustBuffer) throws -> E)? ) throws { switch callStatus.code { - case CALL_SUCCESS: - return - - case CALL_ERROR: - if let errorHandler = errorHandler { - throw try errorHandler(callStatus.errorBuf) - } else { - callStatus.errorBuf.deallocate() - throw UniffiInternalError.unexpectedRustCallError - } - - case CALL_UNEXPECTED_ERROR: - // When the rust code sees a panic, it tries to construct a RustBuffer - // with the message. But if that code panics, then it just sends back - // an empty buffer. - if callStatus.errorBuf.len > 0 { - throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf)) - } else { - callStatus.errorBuf.deallocate() - throw UniffiInternalError.rustPanic("Rust panic") - } - - case CALL_CANCELLED: - fatalError("Cancellation not supported yet") - - default: - throw UniffiInternalError.unexpectedRustCallStatusCode + case CALL_SUCCESS: + return + + case CALL_ERROR: + if let errorHandler = errorHandler { + throw try errorHandler(callStatus.errorBuf) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.unexpectedRustCallError + } + + case CALL_UNEXPECTED_ERROR: + // When the rust code sees a panic, it tries to construct a RustBuffer + // with the message. But if that code panics, then it just sends back + // an empty buffer. + if callStatus.errorBuf.len > 0 { + throw UniffiInternalError.rustPanic(try FfiConverterString.lift(callStatus.errorBuf)) + } else { + callStatus.errorBuf.deallocate() + throw UniffiInternalError.rustPanic("Rust panic") + } + + case CALL_CANCELLED: + fatalError("Cancellation not supported yet") + + default: + throw UniffiInternalError.unexpectedRustCallStatusCode } } @@ -342,7 +343,7 @@ fileprivate class UniffiHandleMap { private var map: [UInt64: T] = [:] private let lock = NSLock() private var currentHandle: UInt64 = 1 - + func insert(obj: T) -> UInt64 { lock.withLock { let handle = currentHandle @@ -351,8 +352,8 @@ fileprivate class UniffiHandleMap { return handle } } - - func get(handle: UInt64) throws -> T { + + func get(handle: UInt64) throws -> T { try lock.withLock { guard let obj = map[handle] else { throw UniffiInternalError.unexpectedStaleHandle @@ -360,7 +361,7 @@ fileprivate class UniffiHandleMap { return obj } } - + @discardableResult func remove(handle: UInt64) throws -> T { try lock.withLock { @@ -370,7 +371,7 @@ fileprivate class UniffiHandleMap { return obj } } - + var count: Int { get { map.count @@ -382,28 +383,28 @@ fileprivate class UniffiHandleMap { // Public interface members begin here. -fileprivate struct FfiConverterUInt16: FfiConverterPrimitive { - typealias FfiType = UInt16 - typealias SwiftType = UInt16 - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt16 { +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 { return try lift(readInt(&buf)) } - - public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + + public static func write(_ value: UInt8, into buf: inout [UInt8]) { writeInt(&buf, lower(value)) } } -fileprivate struct FfiConverterInt32: FfiConverterPrimitive { - typealias FfiType = Int32 - typealias SwiftType = Int32 - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int32 { +fileprivate struct FfiConverterUInt16: FfiConverterPrimitive { + typealias FfiType = UInt16 + typealias SwiftType = UInt16 + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt16 { return try lift(readInt(&buf)) } - - public static func write(_ value: Int32, into buf: inout [UInt8]) { + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { writeInt(&buf, lower(value)) } } @@ -411,11 +412,11 @@ fileprivate struct FfiConverterInt32: FfiConverterPrimitive { fileprivate struct FfiConverterInt64: FfiConverterPrimitive { typealias FfiType = Int64 typealias SwiftType = Int64 - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int64 { return try lift(readInt(&buf)) } - + public static func write(_ value: Int64, into buf: inout [UInt8]) { writeInt(&buf, lower(value)) } @@ -424,19 +425,19 @@ fileprivate struct FfiConverterInt64: FfiConverterPrimitive { fileprivate struct FfiConverterBool : FfiConverter { typealias FfiType = Int8 typealias SwiftType = Bool - + public static func lift(_ value: Int8) throws -> Bool { return value != 0 } - + public static func lower(_ value: Bool) -> Int8 { return value ? 1 : 0 } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Bool { return try lift(readInt(&buf)) } - + public static func write(_ value: Bool, into buf: inout [UInt8]) { writeInt(&buf, lower(value)) } @@ -445,7 +446,7 @@ fileprivate struct FfiConverterBool : FfiConverter { fileprivate struct FfiConverterString: FfiConverter { typealias SwiftType = String typealias FfiType = RustBuffer - + public static func lift(_ value: RustBuffer) throws -> String { defer { value.deallocate() @@ -456,7 +457,7 @@ fileprivate struct FfiConverterString: FfiConverter { let bytes = UnsafeBufferPointer(start: value.data!, count: Int(value.len)) return String(bytes: bytes, encoding: String.Encoding.utf8)! } - + public static func lower(_ value: String) -> RustBuffer { return value.utf8CString.withUnsafeBufferPointer { ptr in // The swift string gives us int8_t, we want uint8_t. @@ -467,12 +468,12 @@ fileprivate struct FfiConverterString: FfiConverter { } } } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> String { let len: Int32 = try readInt(&buf) return String(bytes: try readBytes(&buf, count: Int(len)), encoding: String.Encoding.utf8)! } - + public static func write(_ value: String, into buf: inout [UInt8]) { let len = Int32(value.utf8.count) writeInt(&buf, len) @@ -482,7 +483,7 @@ fileprivate struct FfiConverterString: FfiConverter { fileprivate struct FfiConverterTimestamp: FfiConverterRustBuffer { typealias SwiftType = Date - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Date { let seconds: Int64 = try readInt(&buf) let nanoseconds: UInt32 = try readInt(&buf) @@ -494,7 +495,7 @@ fileprivate struct FfiConverterTimestamp: FfiConverterRustBuffer { return Date.init(timeIntervalSince1970: delta) } } - + public static func write(_ value: Date, into buf: inout [UInt8]) { var delta = value.timeIntervalSince1970 var sign: Int64 = 1 @@ -518,30 +519,34 @@ fileprivate struct FfiConverterTimestamp: FfiConverterRustBuffer { -public protocol OsTunProvider : AnyObject { - - func configureWg(config: WgConfig) throws +/** + * Types observing network changes. + */ +public protocol OsDefaultPathObserver : AnyObject { - func configureNym(config: NymConfig) throws -> Int32 + func onDefaultPathChange(newPath: OsDefaultPath) } -open class OsTunProviderImpl: - OsTunProvider { +/** + * Types observing network changes. + */ +open class OsDefaultPathObserverImpl: + OsDefaultPathObserver { fileprivate let pointer: UnsafeMutableRawPointer! - + /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. public struct NoPointer { public init() {} } - + // TODO: We'd like this to be `private` but for Swifty reasons, // we can't implement `FfiConverter` without making this `required` and we can't // make it `required` without making it `public`. required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { self.pointer = pointer } - + /// This constructor can be used to instantiate a fake object. /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. /// @@ -550,39 +555,31 @@ open class OsTunProviderImpl: public init(noPointer: NoPointer) { self.pointer = nil } - + public func uniffiClonePointer() -> UnsafeMutableRawPointer { - return try! rustCall { uniffi_nym_vpn_lib_fn_clone_ostunprovider(self.pointer, $0) } + return try! rustCall { uniffi_nym_vpn_lib_fn_clone_osdefaultpathobserver(self.pointer, $0) } } // No primary constructor declared for this class. - + deinit { guard let pointer = pointer else { return } - - try! rustCall { uniffi_nym_vpn_lib_fn_free_ostunprovider(pointer, $0) } - } - - - - - open func configureWg(config: WgConfig)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_method_ostunprovider_configure_wg(self.uniffiClonePointer(), - FfiConverterTypeWgConfig.lower(config),$0 - ) - } + + try! rustCall { uniffi_nym_vpn_lib_fn_free_osdefaultpathobserver(pointer, $0) } } + - open func configureNym(config: NymConfig)throws -> Int32 { - return try FfiConverterInt32.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_method_ostunprovider_configure_nym(self.uniffiClonePointer(), - FfiConverterTypeNymConfig.lower(config),$0 - ) - }) - } + +open func onDefaultPathChange(newPath: OsDefaultPath) {try! rustCall() { + uniffi_nym_vpn_lib_fn_method_osdefaultpathobserver_on_default_path_change(self.uniffiClonePointer(), + FfiConverterTypeOSDefaultPath.lower(newPath),$0 + ) +} +} + } // Magic number for the Rust proxy to call using the same mechanism as every other method, // to free the callback once it's dropped by Rust. @@ -593,92 +590,66 @@ private let UNIFFI_CALLBACK_ERROR: Int32 = 1 private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2 // Put the implementation in a struct so we don't pollute the top-level namespace -fileprivate struct UniffiCallbackInterfaceOSTunProvider { - +fileprivate struct UniffiCallbackInterfaceOSDefaultPathObserver { + // Create the VTable using a series of closures. // Swift automatically converts these into C callback functions. - static var vtable: UniffiVTableCallbackInterfaceOsTunProvider = UniffiVTableCallbackInterfaceOsTunProvider( - configureWg: { ( + static var vtable: UniffiVTableCallbackInterfaceOsDefaultPathObserver = UniffiVTableCallbackInterfaceOsDefaultPathObserver( + onDefaultPathChange: { ( uniffiHandle: UInt64, - config: RustBuffer, + newPath: RustBuffer, uniffiOutReturn: UnsafeMutableRawPointer, uniffiCallStatus: UnsafeMutablePointer ) in let makeCall = { () throws -> () in - guard let uniffiObj = try? FfiConverterTypeOSTunProvider.handleMap.get(handle: uniffiHandle) else { + guard let uniffiObj = try? FfiConverterTypeOSDefaultPathObserver.handleMap.get(handle: uniffiHandle) else { throw UniffiInternalError.unexpectedStaleHandle } - return try uniffiObj.configureWg( - config: try FfiConverterTypeWgConfig.lift(config) + return uniffiObj.onDefaultPathChange( + newPath: try FfiConverterTypeOSDefaultPath.lift(newPath) ) } - + let writeReturn = { () } - uniffiTraitInterfaceCallWithError( - callStatus: uniffiCallStatus, - makeCall: makeCall, - writeReturn: writeReturn, - lowerError: FfiConverterTypeFFIError.lower - ) - }, - configureNym: { ( - uniffiHandle: UInt64, - config: RustBuffer, - uniffiOutReturn: UnsafeMutablePointer, - uniffiCallStatus: UnsafeMutablePointer - ) in - let makeCall = { - () throws -> Int32 in - guard let uniffiObj = try? FfiConverterTypeOSTunProvider.handleMap.get(handle: uniffiHandle) else { - throw UniffiInternalError.unexpectedStaleHandle - } - return try uniffiObj.configureNym( - config: try FfiConverterTypeNymConfig.lift(config) - ) - } - - - let writeReturn = { uniffiOutReturn.pointee = FfiConverterInt32.lower($0) } - uniffiTraitInterfaceCallWithError( + uniffiTraitInterfaceCall( callStatus: uniffiCallStatus, makeCall: makeCall, - writeReturn: writeReturn, - lowerError: FfiConverterTypeFFIError.lower + writeReturn: writeReturn ) }, uniffiFree: { (uniffiHandle: UInt64) -> () in - let result = try? FfiConverterTypeOSTunProvider.handleMap.remove(handle: uniffiHandle) + let result = try? FfiConverterTypeOSDefaultPathObserver.handleMap.remove(handle: uniffiHandle) if result == nil { - print("Uniffi callback interface OSTunProvider: handle missing in uniffiFree") + print("Uniffi callback interface OSDefaultPathObserver: handle missing in uniffiFree") } } ) } -private func uniffiCallbackInitOSTunProvider() { - uniffi_nym_vpn_lib_fn_init_callback_vtable_ostunprovider(&UniffiCallbackInterfaceOSTunProvider.vtable) +private func uniffiCallbackInitOSDefaultPathObserver() { + uniffi_nym_vpn_lib_fn_init_callback_vtable_osdefaultpathobserver(&UniffiCallbackInterfaceOSDefaultPathObserver.vtable) } -public struct FfiConverterTypeOSTunProvider: FfiConverter { - fileprivate static var handleMap = UniffiHandleMap() - +public struct FfiConverterTypeOSDefaultPathObserver: FfiConverter { + fileprivate static var handleMap = UniffiHandleMap() + typealias FfiType = UnsafeMutableRawPointer - typealias SwiftType = OsTunProvider - - public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> OsTunProvider { - return OsTunProviderImpl(unsafeFromRawPointer: pointer) + typealias SwiftType = OsDefaultPathObserver + + public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> OsDefaultPathObserver { + return OsDefaultPathObserverImpl(unsafeFromRawPointer: pointer) } - - public static func lower(_ value: OsTunProvider) -> UnsafeMutableRawPointer { + + public static func lower(_ value: OsDefaultPathObserver) -> UnsafeMutableRawPointer { guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else { fatalError("Cast to UnsafeMutableRawPointer failed") } return ptr } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsTunProvider { + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsDefaultPathObserver { let v: UInt64 = try readInt(&buf) // The Rust code won't compile if a pointer won't fit in a UInt64. // We have to go via `UInt` because that's the thing that's the size of a pointer. @@ -688,8 +659,8 @@ public struct FfiConverterTypeOSTunProvider: FfiConverter { } return try lift(ptr!) } - - public static func write(_ value: OsTunProvider, into buf: inout [UInt8]) { + + public static func write(_ value: OsDefaultPathObserver, into buf: inout [UInt8]) { // This fiddling is because `Int` is the thing that's the same size as a pointer. // The Rust code won't compile if a pointer won't fit in a `UInt64`. writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) @@ -699,47 +670,47 @@ public struct FfiConverterTypeOSTunProvider: FfiConverter { -public func FfiConverterTypeOSTunProvider_lift(_ pointer: UnsafeMutableRawPointer) throws -> OsTunProvider { - return try FfiConverterTypeOSTunProvider.lift(pointer) +public func FfiConverterTypeOSDefaultPathObserver_lift(_ pointer: UnsafeMutableRawPointer) throws -> OsDefaultPathObserver { + return try FfiConverterTypeOSDefaultPathObserver.lift(pointer) } -public func FfiConverterTypeOSTunProvider_lower(_ value: OsTunProvider) -> UnsafeMutableRawPointer { - return FfiConverterTypeOSTunProvider.lower(value) +public func FfiConverterTypeOSDefaultPathObserver_lower(_ value: OsDefaultPathObserver) -> UnsafeMutableRawPointer { + return FfiConverterTypeOSDefaultPathObserver.lower(value) } -public protocol TunnelStatusListener : AnyObject { - - func onTunStatusChange(status: TunStatus) - - func onBandwidthStatusChange(status: BandwidthStatus) - - func onConnectionStatusChange(status: ConnectionStatus) +public protocol OsTunProvider : AnyObject { - func onNymVpnStatusChange(status: NymVpnStatus) + /** + * Set network settings including tun, dns, ip. + */ + func setTunnelNetworkSettings(tunnelSettings: TunnelNetworkSettings) async throws - func onExitStatusChange(status: ExitStatus) + /** + * Set or unset the default path observer. + */ + func setDefaultPathObserver(observer: OsDefaultPathObserver?) throws } -open class TunnelStatusListenerImpl: - TunnelStatusListener { +open class OsTunProviderImpl: + OsTunProvider { fileprivate let pointer: UnsafeMutableRawPointer! - + /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. public struct NoPointer { public init() {} } - + // TODO: We'd like this to be `private` but for Swifty reasons, // we can't implement `FfiConverter` without making this `required` and we can't // make it `required` without making it `public`. required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { self.pointer = pointer } - + /// This constructor can be used to instantiate a fake object. /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. /// @@ -748,95 +719,294 @@ open class TunnelStatusListenerImpl: public init(noPointer: NoPointer) { self.pointer = nil } - + public func uniffiClonePointer() -> UnsafeMutableRawPointer { - return try! rustCall { uniffi_nym_vpn_lib_fn_clone_tunnelstatuslistener(self.pointer, $0) } + return try! rustCall { uniffi_nym_vpn_lib_fn_clone_ostunprovider(self.pointer, $0) } } // No primary constructor declared for this class. - + deinit { guard let pointer = pointer else { return } - - try! rustCall { uniffi_nym_vpn_lib_fn_free_tunnelstatuslistener(pointer, $0) } - } - - - - - open func onTunStatusChange(status: TunStatus) {try! rustCall() { - uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_tun_status_change(self.uniffiClonePointer(), - FfiConverterTypeTunStatus.lower(status),$0 - ) - } - } - - open func onBandwidthStatusChange(status: BandwidthStatus) {try! rustCall() { - uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_bandwidth_status_change(self.uniffiClonePointer(), - FfiConverterTypeBandwidthStatus.lower(status),$0 - ) - } - } - - open func onConnectionStatusChange(status: ConnectionStatus) {try! rustCall() { - uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_connection_status_change(self.uniffiClonePointer(), - FfiConverterTypeConnectionStatus.lower(status),$0 - ) - } + + try! rustCall { uniffi_nym_vpn_lib_fn_free_ostunprovider(pointer, $0) } } + - open func onNymVpnStatusChange(status: NymVpnStatus) {try! rustCall() { - uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_nym_vpn_status_change(self.uniffiClonePointer(), - FfiConverterTypeNymVpnStatus.lower(status),$0 - ) - } - } + - open func onExitStatusChange(status: ExitStatus) {try! rustCall() { - uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_exit_status_change(self.uniffiClonePointer(), - FfiConverterTypeExitStatus.lower(status),$0 + /** + * Set network settings including tun, dns, ip. + */ +open func setTunnelNetworkSettings(tunnelSettings: TunnelNetworkSettings)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_nym_vpn_lib_fn_method_ostunprovider_set_tunnel_network_settings( + self.uniffiClonePointer(), + FfiConverterTypeTunnelNetworkSettings.lower(tunnelSettings) + ) + }, + pollFunc: ffi_nym_vpn_lib_rust_future_poll_void, + completeFunc: ffi_nym_vpn_lib_rust_future_complete_void, + freeFunc: ffi_nym_vpn_lib_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeFFIError.lift ) - } - } +} + /** + * Set or unset the default path observer. + */ +open func setDefaultPathObserver(observer: OsDefaultPathObserver?)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { + uniffi_nym_vpn_lib_fn_method_ostunprovider_set_default_path_observer(self.uniffiClonePointer(), + FfiConverterOptionTypeOSDefaultPathObserver.lower(observer),$0 + ) +} +} + } // Put the implementation in a struct so we don't pollute the top-level namespace -fileprivate struct UniffiCallbackInterfaceTunnelStatusListener { - +fileprivate struct UniffiCallbackInterfaceOSTunProvider { + // Create the VTable using a series of closures. // Swift automatically converts these into C callback functions. - static var vtable: UniffiVTableCallbackInterfaceTunnelStatusListener = UniffiVTableCallbackInterfaceTunnelStatusListener( - onTunStatusChange: { ( + static var vtable: UniffiVTableCallbackInterfaceOsTunProvider = UniffiVTableCallbackInterfaceOsTunProvider( + setTunnelNetworkSettings: { ( uniffiHandle: UInt64, - status: RustBuffer, - uniffiOutReturn: UnsafeMutableRawPointer, - uniffiCallStatus: UnsafeMutablePointer + tunnelSettings: RustBuffer, + uniffiFutureCallback: @escaping UniffiForeignFutureCompleteVoid, + uniffiCallbackData: UInt64, + uniffiOutReturn: UnsafeMutablePointer ) in let makeCall = { - () throws -> () in - guard let uniffiObj = try? FfiConverterTypeTunnelStatusListener.handleMap.get(handle: uniffiHandle) else { + () async throws -> () in + guard let uniffiObj = try? FfiConverterTypeOSTunProvider.handleMap.get(handle: uniffiHandle) else { throw UniffiInternalError.unexpectedStaleHandle } - return uniffiObj.onTunStatusChange( - status: try FfiConverterTypeTunStatus.lift(status) + return try await uniffiObj.setTunnelNetworkSettings( + tunnelSettings: try FfiConverterTypeTunnelNetworkSettings.lift(tunnelSettings) ) } - - - let writeReturn = { () } - uniffiTraitInterfaceCall( - callStatus: uniffiCallStatus, + + let uniffiHandleSuccess = { (returnValue: ()) in + uniffiFutureCallback( + uniffiCallbackData, + UniffiForeignFutureStructVoid( + callStatus: RustCallStatus() + ) + ) + } + let uniffiHandleError = { (statusCode, errorBuf) in + uniffiFutureCallback( + uniffiCallbackData, + UniffiForeignFutureStructVoid( + callStatus: RustCallStatus(code: statusCode, errorBuf: errorBuf) + ) + ) + } + let uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( makeCall: makeCall, - writeReturn: writeReturn + handleSuccess: uniffiHandleSuccess, + handleError: uniffiHandleError, + lowerError: FfiConverterTypeFFIError.lower ) + uniffiOutReturn.pointee = uniffiForeignFuture }, - onBandwidthStatusChange: { ( + setDefaultPathObserver: { ( uniffiHandle: UInt64, - status: RustBuffer, + observer: RustBuffer, + uniffiOutReturn: UnsafeMutableRawPointer, + uniffiCallStatus: UnsafeMutablePointer + ) in + let makeCall = { + () throws -> () in + guard let uniffiObj = try? FfiConverterTypeOSTunProvider.handleMap.get(handle: uniffiHandle) else { + throw UniffiInternalError.unexpectedStaleHandle + } + return try uniffiObj.setDefaultPathObserver( + observer: try FfiConverterOptionTypeOSDefaultPathObserver.lift(observer) + ) + } + + + let writeReturn = { () } + uniffiTraitInterfaceCallWithError( + callStatus: uniffiCallStatus, + makeCall: makeCall, + writeReturn: writeReturn, + lowerError: FfiConverterTypeFFIError.lower + ) + }, + uniffiFree: { (uniffiHandle: UInt64) -> () in + let result = try? FfiConverterTypeOSTunProvider.handleMap.remove(handle: uniffiHandle) + if result == nil { + print("Uniffi callback interface OSTunProvider: handle missing in uniffiFree") + } + } + ) +} + +private func uniffiCallbackInitOSTunProvider() { + uniffi_nym_vpn_lib_fn_init_callback_vtable_ostunprovider(&UniffiCallbackInterfaceOSTunProvider.vtable) +} + +public struct FfiConverterTypeOSTunProvider: FfiConverter { + fileprivate static var handleMap = UniffiHandleMap() + + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = OsTunProvider + + public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> OsTunProvider { + return OsTunProviderImpl(unsafeFromRawPointer: pointer) + } + + public static func lower(_ value: OsTunProvider) -> UnsafeMutableRawPointer { + guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else { + fatalError("Cast to UnsafeMutableRawPointer failed") + } + return ptr + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsTunProvider { + let v: UInt64 = try readInt(&buf) + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + public static func write(_ value: OsTunProvider, into buf: inout [UInt8]) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } +} + + + + +public func FfiConverterTypeOSTunProvider_lift(_ pointer: UnsafeMutableRawPointer) throws -> OsTunProvider { + return try FfiConverterTypeOSTunProvider.lift(pointer) +} + +public func FfiConverterTypeOSTunProvider_lower(_ value: OsTunProvider) -> UnsafeMutableRawPointer { + return FfiConverterTypeOSTunProvider.lower(value) +} + + + + +public protocol TunnelStatusListener : AnyObject { + + func onTunStatusChange(status: TunStatus) + + func onBandwidthStatusChange(status: BandwidthStatus) + + func onConnectionStatusChange(status: ConnectionStatus) + + func onNymVpnStatusChange(status: NymVpnStatus) + + func onExitStatusChange(status: ExitStatus) + +} + +open class TunnelStatusListenerImpl: + TunnelStatusListener { + fileprivate let pointer: UnsafeMutableRawPointer! + + /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + public struct NoPointer { + public init() {} + } + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. + required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + + /// This constructor can be used to instantiate a fake object. + /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + /// + /// - Warning: + /// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. + public init(noPointer: NoPointer) { + self.pointer = nil + } + + public func uniffiClonePointer() -> UnsafeMutableRawPointer { + return try! rustCall { uniffi_nym_vpn_lib_fn_clone_tunnelstatuslistener(self.pointer, $0) } + } + // No primary constructor declared for this class. + + deinit { + guard let pointer = pointer else { + return + } + + try! rustCall { uniffi_nym_vpn_lib_fn_free_tunnelstatuslistener(pointer, $0) } + } + + + + +open func onTunStatusChange(status: TunStatus) {try! rustCall() { + uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_tun_status_change(self.uniffiClonePointer(), + FfiConverterTypeTunStatus.lower(status),$0 + ) +} +} + +open func onBandwidthStatusChange(status: BandwidthStatus) {try! rustCall() { + uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_bandwidth_status_change(self.uniffiClonePointer(), + FfiConverterTypeBandwidthStatus.lower(status),$0 + ) +} +} + +open func onConnectionStatusChange(status: ConnectionStatus) {try! rustCall() { + uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_connection_status_change(self.uniffiClonePointer(), + FfiConverterTypeConnectionStatus.lower(status),$0 + ) +} +} + +open func onNymVpnStatusChange(status: NymVpnStatus) {try! rustCall() { + uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_nym_vpn_status_change(self.uniffiClonePointer(), + FfiConverterTypeNymVpnStatus.lower(status),$0 + ) +} +} + +open func onExitStatusChange(status: ExitStatus) {try! rustCall() { + uniffi_nym_vpn_lib_fn_method_tunnelstatuslistener_on_exit_status_change(self.uniffiClonePointer(), + FfiConverterTypeExitStatus.lower(status),$0 + ) +} +} + + +} + + +// Put the implementation in a struct so we don't pollute the top-level namespace +fileprivate struct UniffiCallbackInterfaceTunnelStatusListener { + + // Create the VTable using a series of closures. + // Swift automatically converts these into C callback functions. + static var vtable: UniffiVTableCallbackInterfaceTunnelStatusListener = UniffiVTableCallbackInterfaceTunnelStatusListener( + onTunStatusChange: { ( + uniffiHandle: UInt64, + status: RustBuffer, uniffiOutReturn: UnsafeMutableRawPointer, uniffiCallStatus: UnsafeMutablePointer ) in @@ -845,11 +1015,35 @@ fileprivate struct UniffiCallbackInterfaceTunnelStatusListener { guard let uniffiObj = try? FfiConverterTypeTunnelStatusListener.handleMap.get(handle: uniffiHandle) else { throw UniffiInternalError.unexpectedStaleHandle } - return uniffiObj.onBandwidthStatusChange( - status: try FfiConverterTypeBandwidthStatus.lift(status) + return uniffiObj.onTunStatusChange( + status: try FfiConverterTypeTunStatus.lift(status) ) } + + let writeReturn = { () } + uniffiTraitInterfaceCall( + callStatus: uniffiCallStatus, + makeCall: makeCall, + writeReturn: writeReturn + ) + }, + onBandwidthStatusChange: { ( + uniffiHandle: UInt64, + status: RustBuffer, + uniffiOutReturn: UnsafeMutableRawPointer, + uniffiCallStatus: UnsafeMutablePointer + ) in + let makeCall = { + () throws -> () in + guard let uniffiObj = try? FfiConverterTypeTunnelStatusListener.handleMap.get(handle: uniffiHandle) else { + throw UniffiInternalError.unexpectedStaleHandle + } + return uniffiObj.onBandwidthStatusChange( + status: try FfiConverterTypeBandwidthStatus.lift(status) + ) + } + let writeReturn = { () } uniffiTraitInterfaceCall( @@ -870,10 +1064,10 @@ fileprivate struct UniffiCallbackInterfaceTunnelStatusListener { throw UniffiInternalError.unexpectedStaleHandle } return uniffiObj.onConnectionStatusChange( - status: try FfiConverterTypeConnectionStatus.lift(status) + status: try FfiConverterTypeConnectionStatus.lift(status) ) } - + let writeReturn = { () } uniffiTraitInterfaceCall( @@ -894,10 +1088,10 @@ fileprivate struct UniffiCallbackInterfaceTunnelStatusListener { throw UniffiInternalError.unexpectedStaleHandle } return uniffiObj.onNymVpnStatusChange( - status: try FfiConverterTypeNymVpnStatus.lift(status) + status: try FfiConverterTypeNymVpnStatus.lift(status) ) } - + let writeReturn = { () } uniffiTraitInterfaceCall( @@ -918,10 +1112,10 @@ fileprivate struct UniffiCallbackInterfaceTunnelStatusListener { throw UniffiInternalError.unexpectedStaleHandle } return uniffiObj.onExitStatusChange( - status: try FfiConverterTypeExitStatus.lift(status) + status: try FfiConverterTypeExitStatus.lift(status) ) } - + let writeReturn = { () } uniffiTraitInterfaceCall( @@ -945,21 +1139,21 @@ private func uniffiCallbackInitTunnelStatusListener() { public struct FfiConverterTypeTunnelStatusListener: FfiConverter { fileprivate static var handleMap = UniffiHandleMap() - + typealias FfiType = UnsafeMutableRawPointer typealias SwiftType = TunnelStatusListener - + public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> TunnelStatusListener { return TunnelStatusListenerImpl(unsafeFromRawPointer: pointer) } - + public static func lower(_ value: TunnelStatusListener) -> UnsafeMutableRawPointer { guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else { fatalError("Cast to UnsafeMutableRawPointer failed") } return ptr } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunnelStatusListener { let v: UInt64 = try readInt(&buf) // The Rust code won't compile if a pointer won't fit in a UInt64. @@ -970,7 +1164,7 @@ public struct FfiConverterTypeTunnelStatusListener: FfiConverter { } return try lift(ptr!) } - + public static func write(_ value: TunnelStatusListener, into buf: inout [UInt8]) { // This fiddling is because `Int` is the thing that's the same size as a pointer. // The Rust code won't compile if a pointer won't fit in a `UInt64`. @@ -981,18 +1175,267 @@ public struct FfiConverterTypeTunnelStatusListener: FfiConverter { -public func FfiConverterTypeTunnelStatusListener_lift(_ pointer: UnsafeMutableRawPointer) throws -> TunnelStatusListener { - return try FfiConverterTypeTunnelStatusListener.lift(pointer) +public func FfiConverterTypeTunnelStatusListener_lift(_ pointer: UnsafeMutableRawPointer) throws -> TunnelStatusListener { + return try FfiConverterTypeTunnelStatusListener.lift(pointer) +} + +public func FfiConverterTypeTunnelStatusListener_lower(_ value: TunnelStatusListener) -> UnsafeMutableRawPointer { + return FfiConverterTypeTunnelStatusListener.lower(value) +} + + +public struct DnsSettings { + /** + * DNS IP addresses. + */ + public var servers: [IpAddr] + /** + * DNS server search domains. + */ + public var searchDomains: [String]? + /** + * Which domains to resolve using these DNS settings. + */ + public var matchDomains: [String]? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * DNS IP addresses. + */servers: [IpAddr], + /** + * DNS server search domains. + */searchDomains: [String]?, + /** + * Which domains to resolve using these DNS settings. + */matchDomains: [String]?) { + self.servers = servers + self.searchDomains = searchDomains + self.matchDomains = matchDomains + } +} + + + +extension DnsSettings: Equatable, Hashable { + public static func ==(lhs: DnsSettings, rhs: DnsSettings) -> Bool { + if lhs.servers != rhs.servers { + return false + } + if lhs.searchDomains != rhs.searchDomains { + return false + } + if lhs.matchDomains != rhs.matchDomains { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(servers) + hasher.combine(searchDomains) + hasher.combine(matchDomains) + } +} + + +public struct FfiConverterTypeDnsSettings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DnsSettings { + return + try DnsSettings( + servers: FfiConverterSequenceTypeIpAddr.read(from: &buf), + searchDomains: FfiConverterOptionSequenceString.read(from: &buf), + matchDomains: FfiConverterOptionSequenceString.read(from: &buf) + ) + } + + public static func write(_ value: DnsSettings, into buf: inout [UInt8]) { + FfiConverterSequenceTypeIpAddr.write(value.servers, into: &buf) + FfiConverterOptionSequenceString.write(value.searchDomains, into: &buf) + FfiConverterOptionSequenceString.write(value.matchDomains, into: &buf) + } +} + + +public func FfiConverterTypeDnsSettings_lift(_ buf: RustBuffer) throws -> DnsSettings { + return try FfiConverterTypeDnsSettings.lift(buf) +} + +public func FfiConverterTypeDnsSettings_lower(_ value: DnsSettings) -> RustBuffer { + return FfiConverterTypeDnsSettings.lower(value) +} + + +public struct Ipv4Settings { + /** + * IPv4 addresses that will be set on tunnel interface. + */ + public var addresses: [Ipv4Network] + /** + * Traffic matching these routes will be routed over the tun interface. + */ + public var includedRoutes: [Ipv4Route]? + /** + * Traffic matching these routes will be routed over the primary physical interface. + */ + public var excludedRoutes: [Ipv4Route]? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * IPv4 addresses that will be set on tunnel interface. + */addresses: [Ipv4Network], + /** + * Traffic matching these routes will be routed over the tun interface. + */includedRoutes: [Ipv4Route]?, + /** + * Traffic matching these routes will be routed over the primary physical interface. + */excludedRoutes: [Ipv4Route]?) { + self.addresses = addresses + self.includedRoutes = includedRoutes + self.excludedRoutes = excludedRoutes + } +} + + + +extension Ipv4Settings: Equatable, Hashable { + public static func ==(lhs: Ipv4Settings, rhs: Ipv4Settings) -> Bool { + if lhs.addresses != rhs.addresses { + return false + } + if lhs.includedRoutes != rhs.includedRoutes { + return false + } + if lhs.excludedRoutes != rhs.excludedRoutes { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(addresses) + hasher.combine(includedRoutes) + hasher.combine(excludedRoutes) + } +} + + +public struct FfiConverterTypeIpv4Settings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv4Settings { + return + try Ipv4Settings( + addresses: FfiConverterSequenceTypeIpv4Network.read(from: &buf), + includedRoutes: FfiConverterOptionSequenceTypeIpv4Route.read(from: &buf), + excludedRoutes: FfiConverterOptionSequenceTypeIpv4Route.read(from: &buf) + ) + } + + public static func write(_ value: Ipv4Settings, into buf: inout [UInt8]) { + FfiConverterSequenceTypeIpv4Network.write(value.addresses, into: &buf) + FfiConverterOptionSequenceTypeIpv4Route.write(value.includedRoutes, into: &buf) + FfiConverterOptionSequenceTypeIpv4Route.write(value.excludedRoutes, into: &buf) + } +} + + +public func FfiConverterTypeIpv4Settings_lift(_ buf: RustBuffer) throws -> Ipv4Settings { + return try FfiConverterTypeIpv4Settings.lift(buf) +} + +public func FfiConverterTypeIpv4Settings_lower(_ value: Ipv4Settings) -> RustBuffer { + return FfiConverterTypeIpv4Settings.lower(value) +} + + +public struct Ipv6Settings { + /** + * IPv4 addresses that will be set on tunnel interface. + */ + public var addresses: [Ipv6Network] + /** + * Traffic matching these routes will be routed over the tun interface. + */ + public var includedRoutes: [Ipv6Route]? + /** + * Traffic matching these routes will be routed over the primary physical interface. + */ + public var excludedRoutes: [Ipv6Route]? + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * IPv4 addresses that will be set on tunnel interface. + */addresses: [Ipv6Network], + /** + * Traffic matching these routes will be routed over the tun interface. + */includedRoutes: [Ipv6Route]?, + /** + * Traffic matching these routes will be routed over the primary physical interface. + */excludedRoutes: [Ipv6Route]?) { + self.addresses = addresses + self.includedRoutes = includedRoutes + self.excludedRoutes = excludedRoutes + } +} + + + +extension Ipv6Settings: Equatable, Hashable { + public static func ==(lhs: Ipv6Settings, rhs: Ipv6Settings) -> Bool { + if lhs.addresses != rhs.addresses { + return false + } + if lhs.includedRoutes != rhs.includedRoutes { + return false + } + if lhs.excludedRoutes != rhs.excludedRoutes { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(addresses) + hasher.combine(includedRoutes) + hasher.combine(excludedRoutes) + } +} + + +public struct FfiConverterTypeIpv6Settings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Settings { + return + try Ipv6Settings( + addresses: FfiConverterSequenceTypeIpv6Network.read(from: &buf), + includedRoutes: FfiConverterOptionSequenceTypeIpv6Route.read(from: &buf), + excludedRoutes: FfiConverterOptionSequenceTypeIpv6Route.read(from: &buf) + ) + } + + public static func write(_ value: Ipv6Settings, into buf: inout [UInt8]) { + FfiConverterSequenceTypeIpv6Network.write(value.addresses, into: &buf) + FfiConverterOptionSequenceTypeIpv6Route.write(value.includedRoutes, into: &buf) + FfiConverterOptionSequenceTypeIpv6Route.write(value.excludedRoutes, into: &buf) + } +} + + +public func FfiConverterTypeIpv6Settings_lift(_ buf: RustBuffer) throws -> Ipv6Settings { + return try FfiConverterTypeIpv6Settings.lift(buf) } -public func FfiConverterTypeTunnelStatusListener_lower(_ value: TunnelStatusListener) -> UnsafeMutableRawPointer { - return FfiConverterTypeTunnelStatusListener.lower(value) +public func FfiConverterTypeIpv6Settings_lower(_ value: Ipv6Settings) -> RustBuffer { + return FfiConverterTypeIpv6Settings.lower(value) } public struct Location { public var twoLetterIsoCountryCode: String - + // Default memberwise initializers are never public by default, so we // declare one manually. public init(twoLetterIsoCountryCode: String) { @@ -1009,7 +1452,7 @@ extension Location: Equatable, Hashable { } return true } - + public func hash(into hasher: inout Hasher) { hasher.combine(twoLetterIsoCountryCode) } @@ -1019,11 +1462,11 @@ extension Location: Equatable, Hashable { public struct FfiConverterTypeLocation: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Location { return - try Location( - twoLetterIsoCountryCode: FfiConverterString.read(from: &buf) + try Location( + twoLetterIsoCountryCode: FfiConverterString.read(from: &buf) ) } - + public static func write(_ value: Location, into buf: inout [UInt8]) { FfiConverterString.write(value.twoLetterIsoCountryCode, into: &buf) } @@ -1042,7 +1485,7 @@ public func FfiConverterTypeLocation_lower(_ value: Location) -> RustBuffer { public struct MixConnectionInfo { public var nymAddress: Recipient public var entryGateway: NodeIdentity - + // Default memberwise initializers are never public by default, so we // declare one manually. public init(nymAddress: Recipient, entryGateway: NodeIdentity) { @@ -1063,7 +1506,7 @@ extension MixConnectionInfo: Equatable, Hashable { } return true } - + public func hash(into hasher: inout Hasher) { hasher.combine(nymAddress) hasher.combine(entryGateway) @@ -1074,12 +1517,12 @@ extension MixConnectionInfo: Equatable, Hashable { public struct FfiConverterTypeMixConnectionInfo: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MixConnectionInfo { return - try MixConnectionInfo( - nymAddress: FfiConverterTypeRecipient.read(from: &buf), - entryGateway: FfiConverterTypeNodeIdentity.read(from: &buf) + try MixConnectionInfo( + nymAddress: FfiConverterTypeRecipient.read(from: &buf), + entryGateway: FfiConverterTypeNodeIdentity.read(from: &buf) ) } - + public static func write(_ value: MixConnectionInfo, into buf: inout [UInt8]) { FfiConverterTypeRecipient.write(value.nymAddress, into: &buf) FfiConverterTypeNodeIdentity.write(value.entryGateway, into: &buf) @@ -1100,7 +1543,7 @@ public struct MixExitConnectionInfo { public var exitGateway: NodeIdentity public var exitIpr: Recipient public var ips: IpPair - + // Default memberwise initializers are never public by default, so we // declare one manually. public init(exitGateway: NodeIdentity, exitIpr: Recipient, ips: IpPair) { @@ -1125,7 +1568,7 @@ extension MixExitConnectionInfo: Equatable, Hashable { } return true } - + public func hash(into hasher: inout Hasher) { hasher.combine(exitGateway) hasher.combine(exitIpr) @@ -1137,13 +1580,13 @@ extension MixExitConnectionInfo: Equatable, Hashable { public struct FfiConverterTypeMixExitConnectionInfo: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MixExitConnectionInfo { return - try MixExitConnectionInfo( - exitGateway: FfiConverterTypeNodeIdentity.read(from: &buf), - exitIpr: FfiConverterTypeRecipient.read(from: &buf), - ips: FfiConverterTypeIpPair.read(from: &buf) + try MixExitConnectionInfo( + exitGateway: FfiConverterTypeNodeIdentity.read(from: &buf), + exitIpr: FfiConverterTypeRecipient.read(from: &buf), + ips: FfiConverterTypeIpPair.read(from: &buf) ) } - + public static func write(_ value: MixExitConnectionInfo, into buf: inout [UInt8]) { FfiConverterTypeNodeIdentity.write(value.exitGateway, into: &buf) FfiConverterTypeRecipient.write(value.exitIpr, into: &buf) @@ -1161,206 +1604,203 @@ public func FfiConverterTypeMixExitConnectionInfo_lower(_ value: MixExitConnecti } -public struct NymConfig { - public var ipv4Addr: Ipv4Addr - public var ipv6Addr: Ipv6Addr - public var mtu: UInt16 - public var entryMixnetGatewayIp: IpAddr? - +/** + * Represents a default network route used by the system. + */ +public struct OsDefaultPath { + /** + * Indicates whether the process is able to make connection through the given path. + */ + public var status: OsPathStatus + /** + * Set to true for interfaces that are considered expensive, such as when using cellular data plan. + */ + public var isExpensive: Bool + /** + * Set to true when using a constrained interface, such as when using low-data mode. + */ + public var isConstrained: Bool + // Default memberwise initializers are never public by default, so we // declare one manually. - public init(ipv4Addr: Ipv4Addr, ipv6Addr: Ipv6Addr, mtu: UInt16, entryMixnetGatewayIp: IpAddr?) { - self.ipv4Addr = ipv4Addr - self.ipv6Addr = ipv6Addr - self.mtu = mtu - self.entryMixnetGatewayIp = entryMixnetGatewayIp + public init( + /** + * Indicates whether the process is able to make connection through the given path. + */status: OsPathStatus, + /** + * Set to true for interfaces that are considered expensive, such as when using cellular data plan. + */isExpensive: Bool, + /** + * Set to true when using a constrained interface, such as when using low-data mode. + */isConstrained: Bool) { + self.status = status + self.isExpensive = isExpensive + self.isConstrained = isConstrained } } -extension NymConfig: Equatable, Hashable { - public static func ==(lhs: NymConfig, rhs: NymConfig) -> Bool { - if lhs.ipv4Addr != rhs.ipv4Addr { - return false - } - if lhs.ipv6Addr != rhs.ipv6Addr { +extension OsDefaultPath: Equatable, Hashable { + public static func ==(lhs: OsDefaultPath, rhs: OsDefaultPath) -> Bool { + if lhs.status != rhs.status { return false } - if lhs.mtu != rhs.mtu { + if lhs.isExpensive != rhs.isExpensive { return false } - if lhs.entryMixnetGatewayIp != rhs.entryMixnetGatewayIp { + if lhs.isConstrained != rhs.isConstrained { return false } return true } - + public func hash(into hasher: inout Hasher) { - hasher.combine(ipv4Addr) - hasher.combine(ipv6Addr) - hasher.combine(mtu) - hasher.combine(entryMixnetGatewayIp) + hasher.combine(status) + hasher.combine(isExpensive) + hasher.combine(isConstrained) } } -public struct FfiConverterTypeNymConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NymConfig { +public struct FfiConverterTypeOSDefaultPath: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsDefaultPath { return - try NymConfig( - ipv4Addr: FfiConverterTypeIpv4Addr.read(from: &buf), - ipv6Addr: FfiConverterTypeIpv6Addr.read(from: &buf), - mtu: FfiConverterUInt16.read(from: &buf), - entryMixnetGatewayIp: FfiConverterOptionTypeIpAddr.read(from: &buf) + try OsDefaultPath( + status: FfiConverterTypeOSPathStatus.read(from: &buf), + isExpensive: FfiConverterBool.read(from: &buf), + isConstrained: FfiConverterBool.read(from: &buf) ) } - - public static func write(_ value: NymConfig, into buf: inout [UInt8]) { - FfiConverterTypeIpv4Addr.write(value.ipv4Addr, into: &buf) - FfiConverterTypeIpv6Addr.write(value.ipv6Addr, into: &buf) - FfiConverterUInt16.write(value.mtu, into: &buf) - FfiConverterOptionTypeIpAddr.write(value.entryMixnetGatewayIp, into: &buf) + + public static func write(_ value: OsDefaultPath, into buf: inout [UInt8]) { + FfiConverterTypeOSPathStatus.write(value.status, into: &buf) + FfiConverterBool.write(value.isExpensive, into: &buf) + FfiConverterBool.write(value.isConstrained, into: &buf) } } -public func FfiConverterTypeNymConfig_lift(_ buf: RustBuffer) throws -> NymConfig { - return try FfiConverterTypeNymConfig.lift(buf) +public func FfiConverterTypeOSDefaultPath_lift(_ buf: RustBuffer) throws -> OsDefaultPath { + return try FfiConverterTypeOSDefaultPath.lift(buf) } -public func FfiConverterTypeNymConfig_lower(_ value: NymConfig) -> RustBuffer { - return FfiConverterTypeNymConfig.lower(value) +public func FfiConverterTypeOSDefaultPath_lower(_ value: OsDefaultPath) -> RustBuffer { + return FfiConverterTypeOSDefaultPath.lower(value) } -public struct PeerConfig { - public var publicKey: PublicKey - public var allowedIps: [IpNetwork] - public var endpoint: SocketAddr - public var psk: PresharedKey? - +/** + * Tunnel + network settings + */ +public struct TunnelNetworkSettings { + /** + * Tunnel remote address, which is mostly of decorative value. + */ + public var tunnelRemoteAddress: String + /** + * IPv4 interface settings. + */ + public var ipv4Settings: Ipv4Settings? + /** + * IPv6 interface settings. + */ + public var ipv6Settings: Ipv6Settings? + /** + * DNS settings. + */ + public var dnsSettings: DnsSettings? + /** + * Tunnel device MTU. + */ + public var mtu: UInt16 + // Default memberwise initializers are never public by default, so we // declare one manually. - public init(publicKey: PublicKey, allowedIps: [IpNetwork], endpoint: SocketAddr, psk: PresharedKey?) { - self.publicKey = publicKey - self.allowedIps = allowedIps - self.endpoint = endpoint - self.psk = psk + public init( + /** + * Tunnel remote address, which is mostly of decorative value. + */tunnelRemoteAddress: String, + /** + * IPv4 interface settings. + */ipv4Settings: Ipv4Settings?, + /** + * IPv6 interface settings. + */ipv6Settings: Ipv6Settings?, + /** + * DNS settings. + */dnsSettings: DnsSettings?, + /** + * Tunnel device MTU. + */mtu: UInt16) { + self.tunnelRemoteAddress = tunnelRemoteAddress + self.ipv4Settings = ipv4Settings + self.ipv6Settings = ipv6Settings + self.dnsSettings = dnsSettings + self.mtu = mtu } } -extension PeerConfig: Equatable, Hashable { - public static func ==(lhs: PeerConfig, rhs: PeerConfig) -> Bool { - if lhs.publicKey != rhs.publicKey { - return false - } - if lhs.allowedIps != rhs.allowedIps { +extension TunnelNetworkSettings: Equatable, Hashable { + public static func ==(lhs: TunnelNetworkSettings, rhs: TunnelNetworkSettings) -> Bool { + if lhs.tunnelRemoteAddress != rhs.tunnelRemoteAddress { return false } - if lhs.endpoint != rhs.endpoint { + if lhs.ipv4Settings != rhs.ipv4Settings { return false } - if lhs.psk != rhs.psk { + if lhs.ipv6Settings != rhs.ipv6Settings { return false } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(publicKey) - hasher.combine(allowedIps) - hasher.combine(endpoint) - hasher.combine(psk) - } -} - - -public struct FfiConverterTypePeerConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PeerConfig { - return - try PeerConfig( - publicKey: FfiConverterTypePublicKey.read(from: &buf), - allowedIps: FfiConverterSequenceTypeIpNetwork.read(from: &buf), - endpoint: FfiConverterTypeSocketAddr.read(from: &buf), - psk: FfiConverterOptionTypePresharedKey.read(from: &buf) - ) - } - - public static func write(_ value: PeerConfig, into buf: inout [UInt8]) { - FfiConverterTypePublicKey.write(value.publicKey, into: &buf) - FfiConverterSequenceTypeIpNetwork.write(value.allowedIps, into: &buf) - FfiConverterTypeSocketAddr.write(value.endpoint, into: &buf) - FfiConverterOptionTypePresharedKey.write(value.psk, into: &buf) - } -} - - -public func FfiConverterTypePeerConfig_lift(_ buf: RustBuffer) throws -> PeerConfig { - return try FfiConverterTypePeerConfig.lift(buf) -} - -public func FfiConverterTypePeerConfig_lower(_ value: PeerConfig) -> RustBuffer { - return FfiConverterTypePeerConfig.lower(value) -} - - -public struct TunnelConfig { - public var privateKey: PrivateKey - public var addresses: [IpAddr] - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(privateKey: PrivateKey, addresses: [IpAddr]) { - self.privateKey = privateKey - self.addresses = addresses - } -} - - - -extension TunnelConfig: Equatable, Hashable { - public static func ==(lhs: TunnelConfig, rhs: TunnelConfig) -> Bool { - if lhs.privateKey != rhs.privateKey { + if lhs.dnsSettings != rhs.dnsSettings { return false } - if lhs.addresses != rhs.addresses { + if lhs.mtu != rhs.mtu { return false } return true } - + public func hash(into hasher: inout Hasher) { - hasher.combine(privateKey) - hasher.combine(addresses) + hasher.combine(tunnelRemoteAddress) + hasher.combine(ipv4Settings) + hasher.combine(ipv6Settings) + hasher.combine(dnsSettings) + hasher.combine(mtu) } } -public struct FfiConverterTypeTunnelConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunnelConfig { +public struct FfiConverterTypeTunnelNetworkSettings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunnelNetworkSettings { return - try TunnelConfig( - privateKey: FfiConverterTypePrivateKey.read(from: &buf), - addresses: FfiConverterSequenceTypeIpAddr.read(from: &buf) + try TunnelNetworkSettings( + tunnelRemoteAddress: FfiConverterString.read(from: &buf), + ipv4Settings: FfiConverterOptionTypeIpv4Settings.read(from: &buf), + ipv6Settings: FfiConverterOptionTypeIpv6Settings.read(from: &buf), + dnsSettings: FfiConverterOptionTypeDnsSettings.read(from: &buf), + mtu: FfiConverterUInt16.read(from: &buf) ) } - - public static func write(_ value: TunnelConfig, into buf: inout [UInt8]) { - FfiConverterTypePrivateKey.write(value.privateKey, into: &buf) - FfiConverterSequenceTypeIpAddr.write(value.addresses, into: &buf) + + public static func write(_ value: TunnelNetworkSettings, into buf: inout [UInt8]) { + FfiConverterString.write(value.tunnelRemoteAddress, into: &buf) + FfiConverterOptionTypeIpv4Settings.write(value.ipv4Settings, into: &buf) + FfiConverterOptionTypeIpv6Settings.write(value.ipv6Settings, into: &buf) + FfiConverterOptionTypeDnsSettings.write(value.dnsSettings, into: &buf) + FfiConverterUInt16.write(value.mtu, into: &buf) } } -public func FfiConverterTypeTunnelConfig_lift(_ buf: RustBuffer) throws -> TunnelConfig { - return try FfiConverterTypeTunnelConfig.lift(buf) +public func FfiConverterTypeTunnelNetworkSettings_lift(_ buf: RustBuffer) throws -> TunnelNetworkSettings { + return try FfiConverterTypeTunnelNetworkSettings.lift(buf) } -public func FfiConverterTypeTunnelConfig_lower(_ value: TunnelConfig) -> RustBuffer { - return FfiConverterTypeTunnelConfig.lower(value) +public func FfiConverterTypeTunnelNetworkSettings_lower(_ value: TunnelNetworkSettings) -> RustBuffer { + return FfiConverterTypeTunnelNetworkSettings.lower(value) } @@ -1369,7 +1809,7 @@ public struct UserAgent { public var version: String public var platform: String public var gitCommit: String - + // Default memberwise initializers are never public by default, so we // declare one manually. public init(application: String, version: String, platform: String, gitCommit: String) { @@ -1398,7 +1838,7 @@ extension UserAgent: Equatable, Hashable { } return true } - + public func hash(into hasher: inout Hasher) { hasher.combine(application) hasher.combine(version) @@ -1411,14 +1851,14 @@ extension UserAgent: Equatable, Hashable { public struct FfiConverterTypeUserAgent: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UserAgent { return - try UserAgent( - application: FfiConverterString.read(from: &buf), - version: FfiConverterString.read(from: &buf), - platform: FfiConverterString.read(from: &buf), - gitCommit: FfiConverterString.read(from: &buf) + try UserAgent( + application: FfiConverterString.read(from: &buf), + version: FfiConverterString.read(from: &buf), + platform: FfiConverterString.read(from: &buf), + gitCommit: FfiConverterString.read(from: &buf) ) } - + public static func write(_ value: UserAgent, into buf: inout [UInt8]) { FfiConverterString.write(value.application, into: &buf) FfiConverterString.write(value.version, into: &buf) @@ -1446,7 +1886,7 @@ public struct VpnConfig { public var tunProvider: OsTunProvider public var credentialDataPath: PathBuf? public var tunStatusListener: TunnelStatusListener? - + // Default memberwise initializers are never public by default, so we // declare one manually. public init(apiUrl: Url, vpnApiUrl: Url?, entryGateway: EntryPoint, exitRouter: ExitPoint, enableTwoHop: Bool, tunProvider: OsTunProvider, credentialDataPath: PathBuf?, tunStatusListener: TunnelStatusListener?) { @@ -1466,18 +1906,18 @@ public struct VpnConfig { public struct FfiConverterTypeVPNConfig: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> VpnConfig { return - try VpnConfig( - apiUrl: FfiConverterTypeUrl.read(from: &buf), - vpnApiUrl: FfiConverterOptionTypeUrl.read(from: &buf), - entryGateway: FfiConverterTypeEntryPoint.read(from: &buf), - exitRouter: FfiConverterTypeExitPoint.read(from: &buf), - enableTwoHop: FfiConverterBool.read(from: &buf), - tunProvider: FfiConverterTypeOSTunProvider.read(from: &buf), - credentialDataPath: FfiConverterOptionTypePathBuf.read(from: &buf), - tunStatusListener: FfiConverterOptionTypeTunnelStatusListener.read(from: &buf) + try VpnConfig( + apiUrl: FfiConverterTypeUrl.read(from: &buf), + vpnApiUrl: FfiConverterOptionTypeUrl.read(from: &buf), + entryGateway: FfiConverterTypeEntryPoint.read(from: &buf), + exitRouter: FfiConverterTypeExitPoint.read(from: &buf), + enableTwoHop: FfiConverterBool.read(from: &buf), + tunProvider: FfiConverterTypeOSTunProvider.read(from: &buf), + credentialDataPath: FfiConverterOptionTypePathBuf.read(from: &buf), + tunStatusListener: FfiConverterOptionTypeTunnelStatusListener.read(from: &buf) ) } - + public static func write(_ value: VpnConfig, into buf: inout [UInt8]) { FfiConverterTypeUrl.write(value.apiUrl, into: &buf) FfiConverterOptionTypeUrl.write(value.vpnApiUrl, into: &buf) @@ -1500,92 +1940,11 @@ public func FfiConverterTypeVPNConfig_lower(_ value: VpnConfig) -> RustBuffer { } -public struct WgConfig { - public var tunnel: TunnelConfig - public var peers: [PeerConfig] - public var ipv4Gateway: Ipv4Addr - public var ipv6Gateway: Ipv6Addr? - public var mtu: UInt16 - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(tunnel: TunnelConfig, peers: [PeerConfig], ipv4Gateway: Ipv4Addr, ipv6Gateway: Ipv6Addr?, mtu: UInt16) { - self.tunnel = tunnel - self.peers = peers - self.ipv4Gateway = ipv4Gateway - self.ipv6Gateway = ipv6Gateway - self.mtu = mtu - } -} - - - -extension WgConfig: Equatable, Hashable { - public static func ==(lhs: WgConfig, rhs: WgConfig) -> Bool { - if lhs.tunnel != rhs.tunnel { - return false - } - if lhs.peers != rhs.peers { - return false - } - if lhs.ipv4Gateway != rhs.ipv4Gateway { - return false - } - if lhs.ipv6Gateway != rhs.ipv6Gateway { - return false - } - if lhs.mtu != rhs.mtu { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(tunnel) - hasher.combine(peers) - hasher.combine(ipv4Gateway) - hasher.combine(ipv6Gateway) - hasher.combine(mtu) - } -} - - -public struct FfiConverterTypeWgConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WgConfig { - return - try WgConfig( - tunnel: FfiConverterTypeTunnelConfig.read(from: &buf), - peers: FfiConverterSequenceTypePeerConfig.read(from: &buf), - ipv4Gateway: FfiConverterTypeIpv4Addr.read(from: &buf), - ipv6Gateway: FfiConverterOptionTypeIpv6Addr.read(from: &buf), - mtu: FfiConverterUInt16.read(from: &buf) - ) - } - - public static func write(_ value: WgConfig, into buf: inout [UInt8]) { - FfiConverterTypeTunnelConfig.write(value.tunnel, into: &buf) - FfiConverterSequenceTypePeerConfig.write(value.peers, into: &buf) - FfiConverterTypeIpv4Addr.write(value.ipv4Gateway, into: &buf) - FfiConverterOptionTypeIpv6Addr.write(value.ipv6Gateway, into: &buf) - FfiConverterUInt16.write(value.mtu, into: &buf) - } -} - - -public func FfiConverterTypeWgConfig_lift(_ buf: RustBuffer) throws -> WgConfig { - return try FfiConverterTypeWgConfig.lift(buf) -} - -public func FfiConverterTypeWgConfig_lower(_ value: WgConfig) -> RustBuffer { - return FfiConverterTypeWgConfig.lower(value) -} - - public struct WireguardConnectionInfo { public var gatewayId: NodeIdentity public var publicKey: String public var privateIpv4: Ipv4Addr - + // Default memberwise initializers are never public by default, so we // declare one manually. public init(gatewayId: NodeIdentity, publicKey: String, privateIpv4: Ipv4Addr) { @@ -1610,7 +1969,7 @@ extension WireguardConnectionInfo: Equatable, Hashable { } return true } - + public func hash(into hasher: inout Hasher) { hasher.combine(gatewayId) hasher.combine(publicKey) @@ -1622,13 +1981,13 @@ extension WireguardConnectionInfo: Equatable, Hashable { public struct FfiConverterTypeWireguardConnectionInfo: FfiConverterRustBuffer { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WireguardConnectionInfo { return - try WireguardConnectionInfo( - gatewayId: FfiConverterTypeNodeIdentity.read(from: &buf), - publicKey: FfiConverterString.read(from: &buf), - privateIpv4: FfiConverterTypeIpv4Addr.read(from: &buf) + try WireguardConnectionInfo( + gatewayId: FfiConverterTypeNodeIdentity.read(from: &buf), + publicKey: FfiConverterString.read(from: &buf), + privateIpv4: FfiConverterTypeIpv4Addr.read(from: &buf) ) } - + public static func write(_ value: WireguardConnectionInfo, into buf: inout [UInt8]) { FfiConverterTypeNodeIdentity.write(value.gatewayId, into: &buf) FfiConverterString.write(value.publicKey, into: &buf) @@ -1658,28 +2017,28 @@ public enum BandwidthStatus { public struct FfiConverterTypeBandwidthStatus: FfiConverterRustBuffer { typealias SwiftType = BandwidthStatus - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> BandwidthStatus { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .noBandwidth - + case 2: return .remainingBandwidth(bandwidth: try FfiConverterInt64.read(from: &buf) ) - + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: BandwidthStatus, into buf: inout [UInt8]) { switch value { - - + + case .noBandwidth: writeInt(&buf, Int32(1)) - - + + case let .remainingBandwidth(bandwidth): writeInt(&buf, Int32(2)) FfiConverterInt64.write(bandwidth, into: &buf) @@ -1720,60 +2079,60 @@ public enum ConnectionStatus { public struct FfiConverterTypeConnectionStatus: FfiConverterRustBuffer { typealias SwiftType = ConnectionStatus - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ConnectionStatus { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .entryGatewayDown - + case 2: return .exitGatewayDownIpv4 - + case 3: return .exitGatewayDownIpv6 - + case 4: return .exitGatewayRoutingErrorIpv4 - + case 5: return .exitGatewayRoutingErrorIpv6 - + case 6: return .connectedIpv4 - + case 7: return .connectedIpv6 - + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: ConnectionStatus, into buf: inout [UInt8]) { switch value { - - + + case .entryGatewayDown: writeInt(&buf, Int32(1)) - - + + case .exitGatewayDownIpv4: writeInt(&buf, Int32(2)) - - + + case .exitGatewayDownIpv6: writeInt(&buf, Int32(3)) - - + + case .exitGatewayRoutingErrorIpv4: writeInt(&buf, Int32(4)) - - + + case .exitGatewayRoutingErrorIpv6: writeInt(&buf, Int32(5)) - - + + case .connectedIpv4: writeInt(&buf, Int32(6)) - - + + case .connectedIpv6: writeInt(&buf, Int32(7)) - + } } } @@ -1809,46 +2168,46 @@ public enum EntryPoint { public struct FfiConverterTypeEntryPoint: FfiConverterRustBuffer { typealias SwiftType = EntryPoint - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> EntryPoint { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .gateway(identity: try FfiConverterTypeNodeIdentity.read(from: &buf) ) - + case 2: return .location(location: try FfiConverterString.read(from: &buf) ) - + case 3: return .randomLowLatency - + case 4: return .random - + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: EntryPoint, into buf: inout [UInt8]) { switch value { - - + + case let .gateway(identity): writeInt(&buf, Int32(1)) FfiConverterTypeNodeIdentity.write(identity, into: &buf) - + case let .location(location): writeInt(&buf, Int32(2)) FfiConverterString.write(location, into: &buf) - + case .randomLowLatency: writeInt(&buf, Int32(3)) - - + + case .random: writeInt(&buf, Int32(4)) - + } } } @@ -1884,38 +2243,38 @@ public enum ExitPoint { public struct FfiConverterTypeExitPoint: FfiConverterRustBuffer { typealias SwiftType = ExitPoint - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ExitPoint { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .address(address: try FfiConverterTypeRecipient.read(from: &buf) ) - + case 2: return .gateway(identity: try FfiConverterTypeNodeIdentity.read(from: &buf) ) - + case 3: return .location(location: try FfiConverterString.read(from: &buf) ) - + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: ExitPoint, into buf: inout [UInt8]) { switch value { - - + + case let .address(address): writeInt(&buf, Int32(1)) FfiConverterTypeRecipient.write(address, into: &buf) - + case let .gateway(identity): writeInt(&buf, Int32(2)) FfiConverterTypeNodeIdentity.write(identity, into: &buf) - + case let .location(location): writeInt(&buf, Int32(3)) FfiConverterString.write(location, into: &buf) @@ -1952,28 +2311,28 @@ public enum ExitStatus { public struct FfiConverterTypeExitStatus: FfiConverterRustBuffer { typealias SwiftType = ExitStatus - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> ExitStatus { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .stopped - + case 2: return .failed(error: try FfiConverterString.read(from: &buf) ) - + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: ExitStatus, into buf: inout [UInt8]) { switch value { - - + + case .stopped: writeInt(&buf, Int32(1)) - - + + case let .failed(error): writeInt(&buf, Int32(2)) FfiConverterString.write(error, into: &buf) @@ -1999,7 +2358,7 @@ extension ExitStatus: Equatable, Hashable {} public enum FfiError { - + case InvalidValueUniffi @@ -2021,19 +2380,19 @@ public enum FfiError { public struct FfiConverterTypeFFIError: FfiConverterRustBuffer { typealias SwiftType = FfiError - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> FfiError { let variant: Int32 = try readInt(&buf) switch variant { - - - - + + + + case 1: return .InvalidValueUniffi case 2: return .InvalidCredential case 3: return .VpnApiClientError( inner: try FfiConverterString.read(from: &buf) - ) + ) case 4: return .InvalidPath case 5: return .FdNotFound case 6: return .VpnNotStopped @@ -2042,64 +2401,64 @@ public struct FfiConverterTypeFFIError: FfiConverterRustBuffer { case 9: return .VpnNotRunning case 10: return .LibError( inner: try FfiConverterString.read(from: &buf) - ) + ) case 11: return .GatewayDirectoryError( inner: try FfiConverterString.read(from: &buf) - ) - - default: throw UniffiInternalError.unexpectedEnumCase + ) + + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: FfiError, into buf: inout [UInt8]) { switch value { - - - - - + + + + + case .InvalidValueUniffi: writeInt(&buf, Int32(1)) - - + + case .InvalidCredential: writeInt(&buf, Int32(2)) - - + + case let .VpnApiClientError(inner): writeInt(&buf, Int32(3)) FfiConverterString.write(inner, into: &buf) - + case .InvalidPath: writeInt(&buf, Int32(4)) - - + + case .FdNotFound: writeInt(&buf, Int32(5)) - - + + case .VpnNotStopped: writeInt(&buf, Int32(6)) - - + + case .VpnNotStarted: writeInt(&buf, Int32(7)) - - + + case .VpnAlreadyRunning: writeInt(&buf, Int32(8)) - - + + case .VpnNotRunning: writeInt(&buf, Int32(9)) - - + + case let .LibError(inner): writeInt(&buf, Int32(10)) FfiConverterString.write(inner, into: &buf) - + case let .GatewayDirectoryError(inner): writeInt(&buf, Int32(11)) FfiConverterString.write(inner, into: &buf) @@ -2111,7 +2470,143 @@ public struct FfiConverterTypeFFIError: FfiConverterRustBuffer { extension FfiError: Equatable, Hashable {} -extension FfiError: Error { } +extension FfiError: Foundation.LocalizedError { + public var errorDescription: String? { + String(reflecting: self) + } +} + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum Ipv4Route { + + /** + * Default IPv4 route (0.0.0.0/0) + */ + case `default` + /** + * Individual IPv4 route + */ + case specific(destination: Ipv4Addr, subnetMask: Ipv4Addr, gateway: Ipv4Addr? + ) +} + + +public struct FfiConverterTypeIpv4Route: FfiConverterRustBuffer { + typealias SwiftType = Ipv4Route + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv4Route { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .`default` + + case 2: return .specific(destination: try FfiConverterTypeIpv4Addr.read(from: &buf), subnetMask: try FfiConverterTypeIpv4Addr.read(from: &buf), gateway: try FfiConverterOptionTypeIpv4Addr.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: Ipv4Route, into buf: inout [UInt8]) { + switch value { + + + case .`default`: + writeInt(&buf, Int32(1)) + + + case let .specific(destination,subnetMask,gateway): + writeInt(&buf, Int32(2)) + FfiConverterTypeIpv4Addr.write(destination, into: &buf) + FfiConverterTypeIpv4Addr.write(subnetMask, into: &buf) + FfiConverterOptionTypeIpv4Addr.write(gateway, into: &buf) + + } + } +} + + +public func FfiConverterTypeIpv4Route_lift(_ buf: RustBuffer) throws -> Ipv4Route { + return try FfiConverterTypeIpv4Route.lift(buf) +} + +public func FfiConverterTypeIpv4Route_lower(_ value: Ipv4Route) -> RustBuffer { + return FfiConverterTypeIpv4Route.lower(value) +} + + + +extension Ipv4Route: Equatable, Hashable {} + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum Ipv6Route { + + /** + * Default IPv6 route (::/0) + */ + case `default` + /** + * Individual IPv6 route + */ + case specific(destination: Ipv6Addr, prefixLength: UInt8, gateway: Ipv6Addr? + ) +} + + +public struct FfiConverterTypeIpv6Route: FfiConverterRustBuffer { + typealias SwiftType = Ipv6Route + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Route { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .`default` + + case 2: return .specific(destination: try FfiConverterTypeIpv6Addr.read(from: &buf), prefixLength: try FfiConverterUInt8.read(from: &buf), gateway: try FfiConverterOptionTypeIpv6Addr.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: Ipv6Route, into buf: inout [UInt8]) { + switch value { + + + case .`default`: + writeInt(&buf, Int32(1)) + + + case let .specific(destination,prefixLength,gateway): + writeInt(&buf, Int32(2)) + FfiConverterTypeIpv6Addr.write(destination, into: &buf) + FfiConverterUInt8.write(prefixLength, into: &buf) + FfiConverterOptionTypeIpv6Addr.write(gateway, into: &buf) + + } + } +} + + +public func FfiConverterTypeIpv6Route_lift(_ buf: RustBuffer) throws -> Ipv6Route { + return try FfiConverterTypeIpv6Route.lift(buf) +} + +public func FfiConverterTypeIpv6Route_lower(_ value: Ipv6Route) -> RustBuffer { + return FfiConverterTypeIpv6Route.lower(value) +} + + + +extension Ipv6Route: Equatable, Hashable {} + + // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. @@ -2127,31 +2622,31 @@ public enum NymVpnStatus { public struct FfiConverterTypeNymVpnStatus: FfiConverterRustBuffer { typealias SwiftType = NymVpnStatus - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NymVpnStatus { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .mixConnectInfo(mixConnectionInfo: try FfiConverterTypeMixConnectionInfo.read(from: &buf), mixExitConnectionInfo: try FfiConverterTypeMixExitConnectionInfo.read(from: &buf) ) - + case 2: return .wgConnectInfo(entryConnectionInfo: try FfiConverterTypeWireguardConnectionInfo.read(from: &buf), exitConnectionInfo: try FfiConverterTypeWireguardConnectionInfo.read(from: &buf) ) - + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: NymVpnStatus, into buf: inout [UInt8]) { switch value { - - + + case let .mixConnectInfo(mixConnectionInfo,mixExitConnectionInfo): writeInt(&buf, Int32(1)) FfiConverterTypeMixConnectionInfo.write(mixConnectionInfo, into: &buf) FfiConverterTypeMixExitConnectionInfo.write(mixExitConnectionInfo, into: &buf) - + case let .wgConnectInfo(entryConnectionInfo,exitConnectionInfo): writeInt(&buf, Int32(2)) FfiConverterTypeWireguardConnectionInfo.write(entryConnectionInfo, into: &buf) @@ -2162,17 +2657,114 @@ public struct FfiConverterTypeNymVpnStatus: FfiConverterRustBuffer { } -public func FfiConverterTypeNymVpnStatus_lift(_ buf: RustBuffer) throws -> NymVpnStatus { - return try FfiConverterTypeNymVpnStatus.lift(buf) +public func FfiConverterTypeNymVpnStatus_lift(_ buf: RustBuffer) throws -> NymVpnStatus { + return try FfiConverterTypeNymVpnStatus.lift(buf) +} + +public func FfiConverterTypeNymVpnStatus_lower(_ value: NymVpnStatus) -> RustBuffer { + return FfiConverterTypeNymVpnStatus.lower(value) +} + + + +extension NymVpnStatus: Equatable, Hashable {} + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum OsPathStatus { + + /** + * The path cannot be evaluated. + */ + case invalid + /** + * The path is ready to be used for network connections. + */ + case satisfied + /** + * The path for network connections is not available, either due to lack of network + * connectivity or being prohibited by system policy. + */ + case unsatisfied + /** + * The path is not currently satisfied, but may become satisfied upon a connection attempt. + * This can be due to a service, such as a VPN or a cellular data connection not being activated. + */ + case satisfiable + /** + * Unknown path status was received. + * The raw variant code is contained in associated value. + */ + case unknown(Int64 + ) +} + + +public struct FfiConverterTypeOSPathStatus: FfiConverterRustBuffer { + typealias SwiftType = OsPathStatus + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsPathStatus { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .invalid + + case 2: return .satisfied + + case 3: return .unsatisfied + + case 4: return .satisfiable + + case 5: return .unknown(try FfiConverterInt64.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: OsPathStatus, into buf: inout [UInt8]) { + switch value { + + + case .invalid: + writeInt(&buf, Int32(1)) + + + case .satisfied: + writeInt(&buf, Int32(2)) + + + case .unsatisfied: + writeInt(&buf, Int32(3)) + + + case .satisfiable: + writeInt(&buf, Int32(4)) + + + case let .unknown(v1): + writeInt(&buf, Int32(5)) + FfiConverterInt64.write(v1, into: &buf) + + } + } +} + + +public func FfiConverterTypeOSPathStatus_lift(_ buf: RustBuffer) throws -> OsPathStatus { + return try FfiConverterTypeOSPathStatus.lift(buf) } -public func FfiConverterTypeNymVpnStatus_lower(_ value: NymVpnStatus) -> RustBuffer { - return FfiConverterTypeNymVpnStatus.lower(value) +public func FfiConverterTypeOSPathStatus_lower(_ value: OsPathStatus) -> RustBuffer { + return FfiConverterTypeOSPathStatus.lower(value) } -extension NymVpnStatus: Equatable, Hashable {} +extension OsPathStatus: Equatable, Hashable {} @@ -2191,48 +2783,48 @@ public enum TunStatus { public struct FfiConverterTypeTunStatus: FfiConverterRustBuffer { typealias SwiftType = TunStatus - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunStatus { let variant: Int32 = try readInt(&buf) switch variant { - + case 1: return .up - + case 2: return .down - + case 3: return .initializingClient - + case 4: return .establishingConnection - + case 5: return .disconnecting - + default: throw UniffiInternalError.unexpectedEnumCase } } - + public static func write(_ value: TunStatus, into buf: inout [UInt8]) { switch value { - - + + case .up: writeInt(&buf, Int32(1)) - - + + case .down: writeInt(&buf, Int32(2)) - - + + case .initializingClient: writeInt(&buf, Int32(3)) - - + + case .establishingConnection: writeInt(&buf, Int32(4)) - - + + case .disconnecting: writeInt(&buf, Int32(5)) - + } } } @@ -2254,7 +2846,7 @@ extension TunStatus: Equatable, Hashable {} fileprivate struct FfiConverterOptionTimestamp: FfiConverterRustBuffer { typealias SwiftType = Date? - + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) @@ -2263,7 +2855,7 @@ fileprivate struct FfiConverterOptionTimestamp: FfiConverterRustBuffer { writeInt(&buf, Int8(1)) FfiConverterTimestamp.write(value, into: &buf) } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil @@ -2273,9 +2865,30 @@ fileprivate struct FfiConverterOptionTimestamp: FfiConverterRustBuffer { } } +fileprivate struct FfiConverterOptionTypeOSDefaultPathObserver: FfiConverterRustBuffer { + typealias SwiftType = OsDefaultPathObserver? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeOSDefaultPathObserver.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeOSDefaultPathObserver.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + fileprivate struct FfiConverterOptionTypeTunnelStatusListener: FfiConverterRustBuffer { typealias SwiftType = TunnelStatusListener? - + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) @@ -2284,7 +2897,7 @@ fileprivate struct FfiConverterOptionTypeTunnelStatusListener: FfiConverterRustB writeInt(&buf, Int8(1)) FfiConverterTypeTunnelStatusListener.write(value, into: &buf) } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil @@ -2294,9 +2907,72 @@ fileprivate struct FfiConverterOptionTypeTunnelStatusListener: FfiConverterRustB } } +fileprivate struct FfiConverterOptionTypeDnsSettings: FfiConverterRustBuffer { + typealias SwiftType = DnsSettings? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeDnsSettings.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeDnsSettings.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeIpv4Settings: FfiConverterRustBuffer { + typealias SwiftType = Ipv4Settings? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeIpv4Settings.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeIpv4Settings.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeIpv6Settings: FfiConverterRustBuffer { + typealias SwiftType = Ipv6Settings? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeIpv6Settings.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeIpv6Settings.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + fileprivate struct FfiConverterOptionTypeUserAgent: FfiConverterRustBuffer { typealias SwiftType = UserAgent? - + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) @@ -2305,7 +2981,7 @@ fileprivate struct FfiConverterOptionTypeUserAgent: FfiConverterRustBuffer { writeInt(&buf, Int8(1)) FfiConverterTypeUserAgent.write(value, into: &buf) } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil @@ -2315,85 +2991,127 @@ fileprivate struct FfiConverterOptionTypeUserAgent: FfiConverterRustBuffer { } } -fileprivate struct FfiConverterOptionTypeIpAddr: FfiConverterRustBuffer { - typealias SwiftType = IpAddr? - +fileprivate struct FfiConverterOptionSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [String]? + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) return } writeInt(&buf, Int8(1)) - FfiConverterTypeIpAddr.write(value, into: &buf) + FfiConverterSequenceString.write(value, into: &buf) } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypeIpAddr.read(from: &buf) + case 1: return try FfiConverterSequenceString.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypeIpv6Addr: FfiConverterRustBuffer { - typealias SwiftType = Ipv6Addr? - +fileprivate struct FfiConverterOptionSequenceTypeIpv4Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv4Route]? + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) return } writeInt(&buf, Int8(1)) - FfiConverterTypeIpv6Addr.write(value, into: &buf) + FfiConverterSequenceTypeIpv4Route.write(value, into: &buf) } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypeIpv6Addr.read(from: &buf) + case 1: return try FfiConverterSequenceTypeIpv4Route.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypePathBuf: FfiConverterRustBuffer { - typealias SwiftType = PathBuf? - +fileprivate struct FfiConverterOptionSequenceTypeIpv6Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv6Route]? + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) return } writeInt(&buf, Int8(1)) - FfiConverterTypePathBuf.write(value, into: &buf) + FfiConverterSequenceTypeIpv6Route.write(value, into: &buf) } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypePathBuf.read(from: &buf) + case 1: return try FfiConverterSequenceTypeIpv6Route.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypePresharedKey: FfiConverterRustBuffer { - typealias SwiftType = PresharedKey? - +fileprivate struct FfiConverterOptionTypeIpv4Addr: FfiConverterRustBuffer { + typealias SwiftType = Ipv4Addr? + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) return } writeInt(&buf, Int8(1)) - FfiConverterTypePresharedKey.write(value, into: &buf) + FfiConverterTypeIpv4Addr.write(value, into: &buf) } - + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeIpv4Addr.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeIpv6Addr: FfiConverterRustBuffer { + typealias SwiftType = Ipv6Addr? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeIpv6Addr.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeIpv6Addr.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypePathBuf: FfiConverterRustBuffer { + typealias SwiftType = PathBuf? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypePathBuf.write(value, into: &buf) + } + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypePresharedKey.read(from: &buf) + case 1: return try FfiConverterTypePathBuf.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } @@ -2401,7 +3119,7 @@ fileprivate struct FfiConverterOptionTypePresharedKey: FfiConverterRustBuffer { fileprivate struct FfiConverterOptionTypeUrl: FfiConverterRustBuffer { typealias SwiftType = Url? - + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { writeInt(&buf, Int8(0)) @@ -2410,7 +3128,7 @@ fileprivate struct FfiConverterOptionTypeUrl: FfiConverterRustBuffer { writeInt(&buf, Int8(1)) FfiConverterTypeUrl.write(value, into: &buf) } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil @@ -2420,9 +3138,31 @@ fileprivate struct FfiConverterOptionTypeUrl: FfiConverterRustBuffer { } } +fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [String] + + public static func write(_ value: [String], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterString.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] { + let len: Int32 = try readInt(&buf) + var seq = [String]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterString.read(from: &buf)) + } + return seq + } +} + fileprivate struct FfiConverterSequenceTypeLocation: FfiConverterRustBuffer { typealias SwiftType = [Location] - + public static func write(_ value: [Location], into buf: inout [UInt8]) { let len = Int32(value.count) writeInt(&buf, len) @@ -2430,7 +3170,7 @@ fileprivate struct FfiConverterSequenceTypeLocation: FfiConverterRustBuffer { FfiConverterTypeLocation.write(item, into: &buf) } } - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Location] { let len: Int32 = try readInt(&buf) var seq = [Location]() @@ -2442,67 +3182,111 @@ fileprivate struct FfiConverterSequenceTypeLocation: FfiConverterRustBuffer { } } -fileprivate struct FfiConverterSequenceTypePeerConfig: FfiConverterRustBuffer { - typealias SwiftType = [PeerConfig] - - public static func write(_ value: [PeerConfig], into buf: inout [UInt8]) { +fileprivate struct FfiConverterSequenceTypeIpv4Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv4Route] + + public static func write(_ value: [Ipv4Route], into buf: inout [UInt8]) { let len = Int32(value.count) writeInt(&buf, len) for item in value { - FfiConverterTypePeerConfig.write(item, into: &buf) + FfiConverterTypeIpv4Route.write(item, into: &buf) } } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [PeerConfig] { + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv4Route] { + let len: Int32 = try readInt(&buf) + var seq = [Ipv4Route]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeIpv4Route.read(from: &buf)) + } + return seq + } +} + +fileprivate struct FfiConverterSequenceTypeIpv6Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv6Route] + + public static func write(_ value: [Ipv6Route], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeIpv6Route.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv6Route] { + let len: Int32 = try readInt(&buf) + var seq = [Ipv6Route]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeIpv6Route.read(from: &buf)) + } + return seq + } +} + +fileprivate struct FfiConverterSequenceTypeIpAddr: FfiConverterRustBuffer { + typealias SwiftType = [IpAddr] + + public static func write(_ value: [IpAddr], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeIpAddr.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [IpAddr] { let len: Int32 = try readInt(&buf) - var seq = [PeerConfig]() + var seq = [IpAddr]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - seq.append(try FfiConverterTypePeerConfig.read(from: &buf)) + seq.append(try FfiConverterTypeIpAddr.read(from: &buf)) } return seq } } -fileprivate struct FfiConverterSequenceTypeIpAddr: FfiConverterRustBuffer { - typealias SwiftType = [IpAddr] - - public static func write(_ value: [IpAddr], into buf: inout [UInt8]) { +fileprivate struct FfiConverterSequenceTypeIpv4Network: FfiConverterRustBuffer { + typealias SwiftType = [Ipv4Network] + + public static func write(_ value: [Ipv4Network], into buf: inout [UInt8]) { let len = Int32(value.count) writeInt(&buf, len) for item in value { - FfiConverterTypeIpAddr.write(item, into: &buf) + FfiConverterTypeIpv4Network.write(item, into: &buf) } } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [IpAddr] { + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv4Network] { let len: Int32 = try readInt(&buf) - var seq = [IpAddr]() + var seq = [Ipv4Network]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - seq.append(try FfiConverterTypeIpAddr.read(from: &buf)) + seq.append(try FfiConverterTypeIpv4Network.read(from: &buf)) } return seq } } -fileprivate struct FfiConverterSequenceTypeIpNetwork: FfiConverterRustBuffer { - typealias SwiftType = [IpNetwork] - - public static func write(_ value: [IpNetwork], into buf: inout [UInt8]) { +fileprivate struct FfiConverterSequenceTypeIpv6Network: FfiConverterRustBuffer { + typealias SwiftType = [Ipv6Network] + + public static func write(_ value: [Ipv6Network], into buf: inout [UInt8]) { let len = Int32(value.count) writeInt(&buf, len) for item in value { - FfiConverterTypeIpNetwork.write(item, into: &buf) + FfiConverterTypeIpv6Network.write(item, into: &buf) } } - - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [IpNetwork] { + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv6Network] { let len: Int32 = try readInt(&buf) - var seq = [IpNetwork]() + var seq = [Ipv6Network]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - seq.append(try FfiConverterTypeIpNetwork.read(from: &buf)) + seq.append(try FfiConverterTypeIpv6Network.read(from: &buf)) } return seq } @@ -2518,15 +3302,15 @@ public struct FfiConverterTypeIpAddr: FfiConverter { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IpAddr { return try FfiConverterString.read(from: &buf) } - + public static func write(_ value: IpAddr, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - + public static func lift(_ value: RustBuffer) throws -> IpAddr { return try FfiConverterString.lift(value) } - + public static func lower(_ value: IpAddr) -> RustBuffer { return FfiConverterString.lower(value) } @@ -2543,40 +3327,6 @@ public func FfiConverterTypeIpAddr_lower(_ value: IpAddr) -> RustBuffer { -/** - * Typealias from the type name used in the UDL file to the builtin type. This - * is needed because the UDL type name is used in function/method signatures. - */ -public typealias IpNetwork = String -public struct FfiConverterTypeIpNetwork: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IpNetwork { - return try FfiConverterString.read(from: &buf) - } - - public static func write(_ value: IpNetwork, into buf: inout [UInt8]) { - return FfiConverterString.write(value, into: &buf) - } - - public static func lift(_ value: RustBuffer) throws -> IpNetwork { - return try FfiConverterString.lift(value) - } - - public static func lower(_ value: IpNetwork) -> RustBuffer { - return FfiConverterString.lower(value) - } -} - - -public func FfiConverterTypeIpNetwork_lift(_ value: RustBuffer) throws -> IpNetwork { - return try FfiConverterTypeIpNetwork.lift(value) -} - -public func FfiConverterTypeIpNetwork_lower(_ value: IpNetwork) -> RustBuffer { - return FfiConverterTypeIpNetwork.lower(value) -} - - - /** * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. @@ -2586,15 +3336,15 @@ public struct FfiConverterTypeIpPair: FfiConverter { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IpPair { return try FfiConverterString.read(from: &buf) } - + public static func write(_ value: IpPair, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - + public static func lift(_ value: RustBuffer) throws -> IpPair { return try FfiConverterString.lift(value) } - + public static func lower(_ value: IpPair) -> RustBuffer { return FfiConverterString.lower(value) } @@ -2620,15 +3370,15 @@ public struct FfiConverterTypeIpv4Addr: FfiConverter { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv4Addr { return try FfiConverterString.read(from: &buf) } - + public static func write(_ value: Ipv4Addr, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - + public static func lift(_ value: RustBuffer) throws -> Ipv4Addr { return try FfiConverterString.lift(value) } - + public static func lower(_ value: Ipv4Addr) -> RustBuffer { return FfiConverterString.lower(value) } @@ -2649,66 +3399,32 @@ public func FfiConverterTypeIpv4Addr_lower(_ value: Ipv4Addr) -> RustBuffer { * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias Ipv6Addr = String -public struct FfiConverterTypeIpv6Addr: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Addr { +public typealias Ipv4Network = String +public struct FfiConverterTypeIpv4Network: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv4Network { return try FfiConverterString.read(from: &buf) } - - public static func write(_ value: Ipv6Addr, into buf: inout [UInt8]) { - return FfiConverterString.write(value, into: &buf) - } - - public static func lift(_ value: RustBuffer) throws -> Ipv6Addr { - return try FfiConverterString.lift(value) - } - - public static func lower(_ value: Ipv6Addr) -> RustBuffer { - return FfiConverterString.lower(value) - } -} - - -public func FfiConverterTypeIpv6Addr_lift(_ value: RustBuffer) throws -> Ipv6Addr { - return try FfiConverterTypeIpv6Addr.lift(value) -} - -public func FfiConverterTypeIpv6Addr_lower(_ value: Ipv6Addr) -> RustBuffer { - return FfiConverterTypeIpv6Addr.lower(value) -} - - -/** - * Typealias from the type name used in the UDL file to the builtin type. This - * is needed because the UDL type name is used in function/method signatures. - */ -public typealias NodeIdentity = String -public struct FfiConverterTypeNodeIdentity: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NodeIdentity { - return try FfiConverterString.read(from: &buf) - } - - public static func write(_ value: NodeIdentity, into buf: inout [UInt8]) { + public static func write(_ value: Ipv4Network, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - - public static func lift(_ value: RustBuffer) throws -> NodeIdentity { + + public static func lift(_ value: RustBuffer) throws -> Ipv4Network { return try FfiConverterString.lift(value) } - - public static func lower(_ value: NodeIdentity) -> RustBuffer { + + public static func lower(_ value: Ipv4Network) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypeNodeIdentity_lift(_ value: RustBuffer) throws -> NodeIdentity { - return try FfiConverterTypeNodeIdentity.lift(value) +public func FfiConverterTypeIpv4Network_lift(_ value: RustBuffer) throws -> Ipv4Network { + return try FfiConverterTypeIpv4Network.lift(value) } -public func FfiConverterTypeNodeIdentity_lower(_ value: NodeIdentity) -> RustBuffer { - return FfiConverterTypeNodeIdentity.lower(value) +public func FfiConverterTypeIpv4Network_lower(_ value: Ipv4Network) -> RustBuffer { + return FfiConverterTypeIpv4Network.lower(value) } @@ -2717,32 +3433,32 @@ public func FfiConverterTypeNodeIdentity_lower(_ value: NodeIdentity) -> RustBuf * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PathBuf = String -public struct FfiConverterTypePathBuf: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PathBuf { +public typealias Ipv6Addr = String +public struct FfiConverterTypeIpv6Addr: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Addr { return try FfiConverterString.read(from: &buf) } - - public static func write(_ value: PathBuf, into buf: inout [UInt8]) { + + public static func write(_ value: Ipv6Addr, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - - public static func lift(_ value: RustBuffer) throws -> PathBuf { + + public static func lift(_ value: RustBuffer) throws -> Ipv6Addr { return try FfiConverterString.lift(value) } - - public static func lower(_ value: PathBuf) -> RustBuffer { + + public static func lower(_ value: Ipv6Addr) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePathBuf_lift(_ value: RustBuffer) throws -> PathBuf { - return try FfiConverterTypePathBuf.lift(value) +public func FfiConverterTypeIpv6Addr_lift(_ value: RustBuffer) throws -> Ipv6Addr { + return try FfiConverterTypeIpv6Addr.lift(value) } -public func FfiConverterTypePathBuf_lower(_ value: PathBuf) -> RustBuffer { - return FfiConverterTypePathBuf.lower(value) +public func FfiConverterTypeIpv6Addr_lower(_ value: Ipv6Addr) -> RustBuffer { + return FfiConverterTypeIpv6Addr.lower(value) } @@ -2751,32 +3467,32 @@ public func FfiConverterTypePathBuf_lower(_ value: PathBuf) -> RustBuffer { * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PresharedKey = String -public struct FfiConverterTypePresharedKey: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PresharedKey { +public typealias Ipv6Network = String +public struct FfiConverterTypeIpv6Network: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Network { return try FfiConverterString.read(from: &buf) } - - public static func write(_ value: PresharedKey, into buf: inout [UInt8]) { + + public static func write(_ value: Ipv6Network, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - - public static func lift(_ value: RustBuffer) throws -> PresharedKey { + + public static func lift(_ value: RustBuffer) throws -> Ipv6Network { return try FfiConverterString.lift(value) } - - public static func lower(_ value: PresharedKey) -> RustBuffer { + + public static func lower(_ value: Ipv6Network) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePresharedKey_lift(_ value: RustBuffer) throws -> PresharedKey { - return try FfiConverterTypePresharedKey.lift(value) +public func FfiConverterTypeIpv6Network_lift(_ value: RustBuffer) throws -> Ipv6Network { + return try FfiConverterTypeIpv6Network.lift(value) } -public func FfiConverterTypePresharedKey_lower(_ value: PresharedKey) -> RustBuffer { - return FfiConverterTypePresharedKey.lower(value) +public func FfiConverterTypeIpv6Network_lower(_ value: Ipv6Network) -> RustBuffer { + return FfiConverterTypeIpv6Network.lower(value) } @@ -2785,32 +3501,32 @@ public func FfiConverterTypePresharedKey_lower(_ value: PresharedKey) -> RustBuf * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PrivateKey = String -public struct FfiConverterTypePrivateKey: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PrivateKey { +public typealias NodeIdentity = String +public struct FfiConverterTypeNodeIdentity: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NodeIdentity { return try FfiConverterString.read(from: &buf) } - - public static func write(_ value: PrivateKey, into buf: inout [UInt8]) { + + public static func write(_ value: NodeIdentity, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - - public static func lift(_ value: RustBuffer) throws -> PrivateKey { + + public static func lift(_ value: RustBuffer) throws -> NodeIdentity { return try FfiConverterString.lift(value) } - - public static func lower(_ value: PrivateKey) -> RustBuffer { + + public static func lower(_ value: NodeIdentity) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePrivateKey_lift(_ value: RustBuffer) throws -> PrivateKey { - return try FfiConverterTypePrivateKey.lift(value) +public func FfiConverterTypeNodeIdentity_lift(_ value: RustBuffer) throws -> NodeIdentity { + return try FfiConverterTypeNodeIdentity.lift(value) } -public func FfiConverterTypePrivateKey_lower(_ value: PrivateKey) -> RustBuffer { - return FfiConverterTypePrivateKey.lower(value) +public func FfiConverterTypeNodeIdentity_lower(_ value: NodeIdentity) -> RustBuffer { + return FfiConverterTypeNodeIdentity.lower(value) } @@ -2819,32 +3535,32 @@ public func FfiConverterTypePrivateKey_lower(_ value: PrivateKey) -> RustBuffer * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PublicKey = String -public struct FfiConverterTypePublicKey: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PublicKey { +public typealias PathBuf = String +public struct FfiConverterTypePathBuf: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PathBuf { return try FfiConverterString.read(from: &buf) } - - public static func write(_ value: PublicKey, into buf: inout [UInt8]) { + + public static func write(_ value: PathBuf, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - - public static func lift(_ value: RustBuffer) throws -> PublicKey { + + public static func lift(_ value: RustBuffer) throws -> PathBuf { return try FfiConverterString.lift(value) } - - public static func lower(_ value: PublicKey) -> RustBuffer { + + public static func lower(_ value: PathBuf) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePublicKey_lift(_ value: RustBuffer) throws -> PublicKey { - return try FfiConverterTypePublicKey.lift(value) +public func FfiConverterTypePathBuf_lift(_ value: RustBuffer) throws -> PathBuf { + return try FfiConverterTypePathBuf.lift(value) } -public func FfiConverterTypePublicKey_lower(_ value: PublicKey) -> RustBuffer { - return FfiConverterTypePublicKey.lower(value) +public func FfiConverterTypePathBuf_lower(_ value: PathBuf) -> RustBuffer { + return FfiConverterTypePathBuf.lower(value) } @@ -2858,15 +3574,15 @@ public struct FfiConverterTypeRecipient: FfiConverter { public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Recipient { return try FfiConverterString.read(from: &buf) } - + public static func write(_ value: Recipient, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - + public static func lift(_ value: RustBuffer) throws -> Recipient { return try FfiConverterString.lift(value) } - + public static func lower(_ value: Recipient) -> RustBuffer { return FfiConverterString.lower(value) } @@ -2883,40 +3599,6 @@ public func FfiConverterTypeRecipient_lower(_ value: Recipient) -> RustBuffer { -/** - * Typealias from the type name used in the UDL file to the builtin type. This - * is needed because the UDL type name is used in function/method signatures. - */ -public typealias SocketAddr = String -public struct FfiConverterTypeSocketAddr: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SocketAddr { - return try FfiConverterString.read(from: &buf) - } - - public static func write(_ value: SocketAddr, into buf: inout [UInt8]) { - return FfiConverterString.write(value, into: &buf) - } - - public static func lift(_ value: RustBuffer) throws -> SocketAddr { - return try FfiConverterString.lift(value) - } - - public static func lower(_ value: SocketAddr) -> RustBuffer { - return FfiConverterString.lower(value) - } -} - - -public func FfiConverterTypeSocketAddr_lift(_ value: RustBuffer) throws -> SocketAddr { - return try FfiConverterTypeSocketAddr.lift(value) -} - -public func FfiConverterTypeSocketAddr_lower(_ value: SocketAddr) -> RustBuffer { - return FfiConverterTypeSocketAddr.lower(value) -} - - - /** @@ -2927,22 +3609,22 @@ public typealias Url = URL public struct FfiConverterTypeUrl: FfiConverter { - + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Url { let builtinValue = try FfiConverterString.read(from: &buf) return URL(string: builtinValue)! } - + public static func write(_ value: Url, into buf: inout [UInt8]) { let builtinValue = String(describing: value) return FfiConverterString.write(builtinValue, into: &buf) } - + public static func lift(_ value: RustBuffer) throws -> Url { let builtinValue = try FfiConverterString.lift(value) return URL(string: builtinValue)! } - + public static func lower(_ value: Url) -> RustBuffer { let builtinValue = String(describing: value) return FfiConverterString.lower(builtinValue) @@ -2958,52 +3640,164 @@ public func FfiConverterTypeUrl_lower(_ value: Url) -> RustBuffer { return FfiConverterTypeUrl.lower(value) } +private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0 +private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1 + +fileprivate let uniffiContinuationHandleMap = UniffiHandleMap>() + +fileprivate func uniffiRustCallAsync( + rustFutureFunc: () -> UInt64, + pollFunc: (UInt64, @escaping UniffiRustFutureContinuationCallback, UInt64) -> (), + completeFunc: (UInt64, UnsafeMutablePointer) -> F, + freeFunc: (UInt64) -> (), + liftFunc: (F) throws -> T, + errorHandler: ((RustBuffer) throws -> Swift.Error)? +) async throws -> T { + // Make sure to call uniffiEnsureInitialized() since future creation doesn't have a + // RustCallStatus param, so doesn't use makeRustCall() + uniffiEnsureInitialized() + let rustFuture = rustFutureFunc() + defer { + freeFunc(rustFuture) + } + var pollResult: Int8; + repeat { + pollResult = await withUnsafeContinuation { + pollFunc( + rustFuture, + uniffiFutureContinuationCallback, + uniffiContinuationHandleMap.insert(obj: $0) + ) + } + } while pollResult != UNIFFI_RUST_FUTURE_POLL_READY + + return try liftFunc(makeRustCall( + { completeFunc(rustFuture, $0) }, + errorHandler: errorHandler + )) +} + +// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They +// lift the return value or error and resume the suspended function. +fileprivate func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) { + if let continuation = try? uniffiContinuationHandleMap.remove(handle: handle) { + continuation.resume(returning: pollResult) + } else { + print("uniffiFutureContinuationCallback invalid handle") + } +} +private func uniffiTraitInterfaceCallAsync( + makeCall: @escaping () async throws -> T, + handleSuccess: @escaping (T) -> (), + handleError: @escaping (Int8, RustBuffer) -> () +) -> UniffiForeignFuture { + let task = Task { + do { + handleSuccess(try await makeCall()) + } catch { + handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error))) + } + } + let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task) + return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree) + +} + +private func uniffiTraitInterfaceCallAsyncWithError( + makeCall: @escaping () async throws -> T, + handleSuccess: @escaping (T) -> (), + handleError: @escaping (Int8, RustBuffer) -> (), + lowerError: @escaping (E) -> RustBuffer +) -> UniffiForeignFuture { + let task = Task { + do { + handleSuccess(try await makeCall()) + } catch let error as E { + handleError(CALL_ERROR, lowerError(error)) + } catch { + handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error))) + } + } + let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task) + return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree) +} + +// Borrow the callback handle map implementation to store foreign future handles +// TODO: consolidate the handle-map code (https://github.com/mozilla/uniffi-rs/pull/1823) +fileprivate var UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = UniffiHandleMap() + +// Protocol for tasks that handle foreign futures. +// +// Defining a protocol allows all tasks to be stored in the same handle map. This can't be done +// with the task object itself, since has generic parameters. +protocol UniffiForeignFutureTask { + func cancel() +} + +extension Task: UniffiForeignFutureTask {} + +private func uniffiForeignFutureFree(handle: UInt64) { + do { + let task = try UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle: handle) + // Set the cancellation flag on the task. If it's still running, the code can check the + // cancellation flag or call `Task.checkCancellation()`. If the task has completed, this is + // a no-op. + task.cancel() + } catch { + print("uniffiForeignFutureFree: handle missing from handlemap") + } +} + +// For testing +public func uniffiForeignFutureHandleCountNymVpnLib() -> Int { + UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.count +} public func checkCredential(credential: String)throws -> Date? { return try FfiConverterOptionTimestamp.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_func_checkcredential( - FfiConverterString.lower(credential),$0 - ) - }) + uniffi_nym_vpn_lib_fn_func_checkcredential( + FfiConverterString.lower(credential),$0 + ) +}) } public func getGatewayCountries(apiUrl: Url, nymVpnApiUrl: Url?, exitOnly: Bool, userAgent: UserAgent?)throws -> [Location] { return try FfiConverterSequenceTypeLocation.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_func_getgatewaycountries( - FfiConverterTypeUrl.lower(apiUrl), - FfiConverterOptionTypeUrl.lower(nymVpnApiUrl), - FfiConverterBool.lower(exitOnly), - FfiConverterOptionTypeUserAgent.lower(userAgent),$0 - ) - }) + uniffi_nym_vpn_lib_fn_func_getgatewaycountries( + FfiConverterTypeUrl.lower(apiUrl), + FfiConverterOptionTypeUrl.lower(nymVpnApiUrl), + FfiConverterBool.lower(exitOnly), + FfiConverterOptionTypeUserAgent.lower(userAgent),$0 + ) +}) } public func getLowLatencyEntryCountry(apiUrl: Url, vpnApiUrl: Url?, harbourMasterUrl: Url?)throws -> Location { return try FfiConverterTypeLocation.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_func_getlowlatencyentrycountry( - FfiConverterTypeUrl.lower(apiUrl), - FfiConverterOptionTypeUrl.lower(vpnApiUrl), - FfiConverterOptionTypeUrl.lower(harbourMasterUrl),$0 - ) - }) + uniffi_nym_vpn_lib_fn_func_getlowlatencyentrycountry( + FfiConverterTypeUrl.lower(apiUrl), + FfiConverterOptionTypeUrl.lower(vpnApiUrl), + FfiConverterOptionTypeUrl.lower(harbourMasterUrl),$0 + ) +}) } public func getLowLatencyEntryCountryUserAgent(apiUrl: Url, vpnApiUrl: Url?, harbourMasterUrl: Url?, userAgent: UserAgent)throws -> Location { return try FfiConverterTypeLocation.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_func_getlowlatencyentrycountryuseragent( - FfiConverterTypeUrl.lower(apiUrl), - FfiConverterOptionTypeUrl.lower(vpnApiUrl), - FfiConverterOptionTypeUrl.lower(harbourMasterUrl), - FfiConverterTypeUserAgent.lower(userAgent),$0 - ) - }) + uniffi_nym_vpn_lib_fn_func_getlowlatencyentrycountryuseragent( + FfiConverterTypeUrl.lower(apiUrl), + FfiConverterOptionTypeUrl.lower(vpnApiUrl), + FfiConverterOptionTypeUrl.lower(harbourMasterUrl), + FfiConverterTypeUserAgent.lower(userAgent),$0 + ) +}) } public func importCredential(credential: String, path: String)throws -> Date? { return try FfiConverterOptionTimestamp.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_func_importcredential( - FfiConverterString.lower(credential), - FfiConverterString.lower(path),$0 - ) - }) + uniffi_nym_vpn_lib_fn_func_importcredential( + FfiConverterString.lower(credential), + FfiConverterString.lower(path),$0 + ) +}) } -public func runVpn(config: VpnConfig)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_func_runvpn( +public func startVpn(config: VpnConfig)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { + uniffi_nym_vpn_lib_fn_func_startvpn( FfiConverterTypeVPNConfig.lower(config),$0 ) } @@ -3019,9 +3813,9 @@ private enum InitializationResult { case contractVersionMismatch case apiChecksumMismatch } -// Use a global variables to perform the versioning checks. Swift ensures that +// Use a global variable to perform the versioning checks. Swift ensures that // the code inside is only computed once. -private var initializationResult: InitializationResult { +private var initializationResult: InitializationResult = { // Get the bindings contract version from our ComponentInterface let bindings_contract_version = 26 // Get the scaffolding contract version by calling the into the dylib @@ -3044,16 +3838,19 @@ private var initializationResult: InitializationResult { if (uniffi_nym_vpn_lib_checksum_func_importcredential() != 8591) { return InitializationResult.apiChecksumMismatch } - if (uniffi_nym_vpn_lib_checksum_func_runvpn() != 2496) { + if (uniffi_nym_vpn_lib_checksum_func_startvpn() != 17465) { return InitializationResult.apiChecksumMismatch } if (uniffi_nym_vpn_lib_checksum_func_stopvpn() != 23819) { return InitializationResult.apiChecksumMismatch } - if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_configure_wg() != 61728) { + if (uniffi_nym_vpn_lib_checksum_method_osdefaultpathobserver_on_default_path_change() != 43452) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_set_tunnel_network_settings() != 48304) { return InitializationResult.apiChecksumMismatch } - if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_configure_nym() != 42844) { + if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_set_default_path_observer() != 18569) { return InitializationResult.apiChecksumMismatch } if (uniffi_nym_vpn_lib_checksum_method_tunnelstatuslistener_on_tun_status_change() != 55105) { @@ -3071,11 +3868,12 @@ private var initializationResult: InitializationResult { if (uniffi_nym_vpn_lib_checksum_method_tunnelstatuslistener_on_exit_status_change() != 8499) { return InitializationResult.apiChecksumMismatch } - + + uniffiCallbackInitOSDefaultPathObserver() uniffiCallbackInitOSTunProvider() uniffiCallbackInitTunnelStatusListener() return InitializationResult.ok -} +}() private func uniffiEnsureInitialized() { switch initializationResult { @@ -3088,4 +3886,4 @@ private func uniffiEnsureInitialized() { } } -// swiftlint:enable all +// swiftlint:enable all \ No newline at end of file diff --git a/nym-vpn-apple/NymMixnetTunnel/PacketTunnelProvider.swift b/nym-vpn-apple/NymMixnetTunnel/PacketTunnelProvider.swift index 05f29eabdb..55b85f80f6 100644 --- a/nym-vpn-apple/NymMixnetTunnel/PacketTunnelProvider.swift +++ b/nym-vpn-apple/NymMixnetTunnel/PacketTunnelProvider.swift @@ -4,17 +4,24 @@ import NymLogger import MixnetLibrary import TunnelMixnet import Tunnels +import ConfigurationManager + +enum TunnelEvent { + case statusUpdate(TunStatus) +} class PacketTunnelProvider: NEPacketTunnelProvider { - private lazy var mixnetTunnelProvider = MixnetTunnelProvider() - private lazy var mixnetAdapter: MixnetAdapter = { - MixnetAdapter( - with: self, - mixnetTunnelProvider: mixnetTunnelProvider - ) - }() + private static var defaultPathObserverContext = 0 + private lazy var logger = Logger(label: "MixnetTunnel") + private let eventStream: AsyncStream + private let eventContinuation: AsyncStream.Continuation + + private let stateLock = NSLock() + private var defaultPathObserver: (any OsDefaultPathObserver)? + private var installedDefaultPathObserver = false + override init() { LoggingSystem.bootstrap { label in let fileLogHandler = FileLogHandler(label: label) @@ -29,84 +36,154 @@ class PacketTunnelProvider: NEPacketTunnelProvider { return fileLogHandler #endif } + + let (eventStream, eventContinuation) = AsyncStream.makeStream() + self.eventStream = eventStream + self.eventContinuation = eventContinuation + } + + deinit { + removeDefaultPathObserver() } override func startTunnel(options: [String: NSObject]? = nil) async throws { - logger.log(level: .info, "Start tunnel...") - guard - let tunnelProviderProtocol = self.protocolConfiguration as? NETunnelProviderProtocol, - let mixnetConfig = tunnelProviderProtocol.asMixnetConfig() - else { - logger.log(level: .info, "Start tunnel: invalid saved configuration") + logger.info("Start tunnel...") + + setup() + + guard let tunnelProviderProtocol = protocolConfiguration as? NETunnelProviderProtocol, + let mixnetConfig = tunnelProviderProtocol.asMixnetConfig() else { + logger.error("Failed to obtain tunnel configuration") throw PacketTunnelProviderError.invalidSavedConfiguration } - let callback: () -> Void = { [weak self] in - guard let config = self?.mixnetTunnelProvider.nymConfig - else { - return - } + let vpnConfig = try mixnetConfig.asVpnConfig(tunProvider: self, tunStatusListener: self) - self?.configure(with: config) - self?.mixnetTunnelProvider.fileDescriptor = self?.mixnetAdapter.tunnelFileDescriptor - self?.logger.log( - level: .info, - "Start tunnel: \(String(describing: self?.mixnetAdapter.tunnelFileDescriptor))" - ) + logger.info("Starting backend") + do { + try startVpn(config: vpnConfig) + } catch { + throw PacketTunnelProviderError.backendStartFailure } - mixnetTunnelProvider.nymOnConfigure = callback + logger.info("Backend is up an running...") - do { - self.logger.log(level: .info, "Start tunnel: start") - try mixnetAdapter.start( - with: mixnetConfig.asVpnConfig(mixnetTunnelProvider: mixnetAdapter.mixnetTunnelProvider) - ) - } catch let error { - logger.log(level: .error, "Start tunnel: \(error)") - throw error + for await event in eventStream { + switch event { + case .statusUpdate(.up): + self.logger.debug("Tunnel is up.") + // Stop blocking startTunnel() to avoid being terminated due to system 60s timeout + return + + case .statusUpdate(.establishingConnection): + self.logger.debug("Tunnel is establishing connection.") + + case .statusUpdate(.down): + throw PacketTunnelProviderError.backendStartFailure + + case .statusUpdate(.initializingClient): + self.logger.debug("Initializing the client") + + case .statusUpdate(.disconnecting): + self.logger.debug("Disconnecting?") + } } - logger.log(level: .info, "Start tunnel: connected") } override func stopTunnel(with reason: NEProviderStopReason) async { + logger.info("Stop tunnel...") + do { - try mixnetAdapter.stop() - logger.log(level: .error, "Stop tunnel reason: \(reason)") + try stopVpn() } catch let error { - logger.log(level: .error, "Stop tunnel reason: \(reason), error: \(error)") + logger.error("Failed to stop the tunnel: \(error)") } } } -private extension PacketTunnelProvider { - func configure(with config: NymConfig) { - let networkSettings = MixnetTunnelSettingsGenerator(nymConfig: config).generateNetworkSettings() +extension PacketTunnelProvider { + + func setup() { do { - try setNetworkSettings(networkSettings) + try ConfigurationManager.setEnvVariables() } catch { - logger.log(level: .error, "Configure error: \(error)") + self.logger.error("Failed to set environment: \(error)") } + addDefaultPathObserver() + } + + func addDefaultPathObserver() { + guard !installedDefaultPathObserver else { return } + installedDefaultPathObserver = true + self.addObserver(self, forKeyPath: #keyPath(defaultPath), context: &Self.defaultPathObserverContext) } - func setNetworkSettings(_ networkSettings: NEPacketTunnelNetworkSettings) throws { - var systemError: Error? - let condition = NSCondition() + func removeDefaultPathObserver() { + guard installedDefaultPathObserver else { return } + installedDefaultPathObserver = false + self.removeObserver(self, forKeyPath: #keyPath(defaultPath), context: &Self.defaultPathObserverContext) + } + + func notifyDefaultPathObserver() { + guard let defaultPath else { return } - condition.lock() - defer { condition.unlock() } + let observer = stateLock.withLock { defaultPathObserver } + observer?.onDefaultPathChange(newPath: defaultPath.asOsDefaultPath()) + } - setTunnelNetworkSettings(networkSettings) { error in - systemError = error - condition.signal() + // swiftlint:disable:next block_based_kvo + override func observeValue( + forKeyPath keyPath: String?, + of object: Any?, + change: [NSKeyValueChangeKey: Any]?, + context: UnsafeMutableRawPointer? + ) { + if keyPath == #keyPath(defaultPath) && context == &Self.defaultPathObserverContext { + notifyDefaultPathObserver() + } else { + super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } + } +} + +extension PacketTunnelProvider: TunnelStatusListener { + func onBandwidthStatusChange(status: BandwidthStatus) { + // todo: implement + } + + func onConnectionStatusChange(status: ConnectionStatus) { + // todo: implement + } - let setTunnelNetworkSettingsTimeout: TimeInterval = 5 // seconds + func onNymVpnStatusChange(status: NymVpnStatus) { + // todo: implement + } + + func onExitStatusChange(status: ExitStatus) { + // todo: implement + } + + func onTunStatusChange(status: TunStatus) { + eventContinuation.yield(.statusUpdate(status)) + } +} - if !condition.wait(until: Date().addingTimeInterval(setTunnelNetworkSettingsTimeout)) { - logger.log(level: .error, "setTunnelNetworkSettings timed out") +extension PacketTunnelProvider: OsTunProvider { + func setDefaultPathObserver(observer: (any OsDefaultPathObserver)?) throws { + stateLock.withLock { + defaultPathObserver = observer } + } + + func setTunnelNetworkSettings(tunnelSettings: TunnelNetworkSettings) async throws { + do { + let networkSettings = tunnelSettings.asPacketTunnelNetworkSettings() + + self.logger.debug("Set network settings: \(networkSettings)") + + try await setTunnelNetworkSettings(networkSettings) + } catch { + self.logger.error("Failed to set tunnel network settings: \(error)") - if let error = systemError { throw error } } diff --git a/nym-vpn-apple/NymMixnetTunnel/TunnelSettingsConversions.swift b/nym-vpn-apple/NymMixnetTunnel/TunnelSettingsConversions.swift new file mode 100644 index 0000000000..022b521444 --- /dev/null +++ b/nym-vpn-apple/NymMixnetTunnel/TunnelSettingsConversions.swift @@ -0,0 +1,134 @@ +// swiftlint:disable:this file_name +// +// TunnelSettingsConversions.swift +// NymMixnetTunnel +// +// Created by pronebird on 29/8/24. +// + +import Foundation +import NetworkExtension +import MixnetLibrary +import TunnelMixnet + +extension TunnelNetworkSettings { + func asPacketTunnelNetworkSettings() -> NEPacketTunnelNetworkSettings { + let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: tunnelRemoteAddress) + networkSettings.ipv4Settings = ipv4Settings?.asNEIPv4Settings() + networkSettings.ipv6Settings = ipv6Settings?.asNEIPv6Settings() + networkSettings.mtu = NSNumber(value: mtu) + networkSettings.dnsSettings = dnsSettings?.asNEDNSSettings() + + return networkSettings + } +} + +extension DnsSettings { + func asNEDNSSettings() -> NEDNSSettings { + let dnsSettings = NEDNSSettings(servers: servers) + dnsSettings.searchDomains = searchDomains + dnsSettings.matchDomains = matchDomains + return dnsSettings + } +} + +extension Ipv4Settings { + func asNEIPv4Settings() -> NEIPv4Settings { + var addresses = [String]() + var netmasks = [String]() + + for address in self.addresses { + if let addrRange = IPAddressRange(from: address) { + if let ipv4Addr = addrRange.address as? IPv4Address { + addresses.append("\(ipv4Addr)") + netmasks.append("\(addrRange.subnetMask())") + } + } + } + + let ipv4Settings = NEIPv4Settings(addresses: addresses, subnetMasks: netmasks) + ipv4Settings.includedRoutes = includedRoutes?.map { $0.asNEIPv4Route() } + ipv4Settings.excludedRoutes = excludedRoutes?.map { $0.asNEIPv4Route() } + + return ipv4Settings + } +} + +extension Ipv6Settings { + func asNEIPv6Settings() -> NEIPv6Settings { + var addresses = [String]() + var networkPrefixes = [NSNumber]() + + for address in self.addresses { + if let addrRange = IPAddressRange(from: address) { + if let ipv6Addr = addrRange.address as? IPv6Address { + addresses.append("\(ipv6Addr)") + networkPrefixes.append(NSNumber(value: addrRange.networkPrefixLength)) + } + } + } + + let ipv6Settings = NEIPv6Settings(addresses: addresses, networkPrefixLengths: networkPrefixes) + ipv6Settings.includedRoutes = includedRoutes?.map { $0.asNEIPv6Route() } + ipv6Settings.excludedRoutes = excludedRoutes?.map { $0.asNEIPv6Route() } + return ipv6Settings + } +} + +extension Ipv4Route { + func asNEIPv4Route() -> NEIPv4Route { + switch self { + case .default: + return NEIPv4Route.default() + + case let .specific(destination, subnetMask, gateway): + let ipv4Route = NEIPv4Route(destinationAddress: destination, subnetMask: subnetMask) + ipv4Route.gatewayAddress = gateway + return ipv4Route + } + } +} + +extension Ipv6Route { + func asNEIPv6Route() -> NEIPv6Route { + switch self { + case .default: + return NEIPv6Route.default() + + case let .specific(destination, prefixLength, gateway): + let ipv6Route = NEIPv6Route( + destinationAddress: destination, + networkPrefixLength: NSNumber(value: prefixLength) + ) + ipv6Route.gatewayAddress = gateway + return ipv6Route + } + } +} + +extension NWPath { + func asOsDefaultPath() -> OsDefaultPath { + OsDefaultPath( + status: status.asOsPathStatus(), + isExpensive: isExpensive, + isConstrained: isConstrained + ) + } +} + +extension NWPathStatus { + func asOsPathStatus() -> OsPathStatus { + switch self { + case .invalid: + return .invalid + case .satisfiable: + return .satisfiable + case .satisfied: + return .satisfied + case .unsatisfied: + return .unsatisfied + @unknown default: + return .unknown(Int64(rawValue)) + } + } +} diff --git a/nym-vpn-apple/NymVPN.xcodeproj/project.pbxproj b/nym-vpn-apple/NymVPN.xcodeproj/project.pbxproj index 5ecfb7c82b..852a018336 100644 --- a/nym-vpn-apple/NymVPN.xcodeproj/project.pbxproj +++ b/nym-vpn-apple/NymVPN.xcodeproj/project.pbxproj @@ -7,7 +7,17 @@ objects = { /* Begin PBXBuildFile section */ - 5886CDF52C6CB544001F9651 /* Constants in Frameworks */ = {isa = PBXBuildFile; productRef = 5886CDF42C6CB544001F9651 /* Constants */; }; + 582C30882C80B8E7008CF551 /* canary.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12C2C8077B600B319B5 /* canary.env */; }; + 582C30892C80B8E7008CF551 /* mainnet.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12E2C807B8E00B319B5 /* mainnet.env */; }; + 582C308A2C80B8E7008CF551 /* sandbox.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12F2C807B8E00B319B5 /* sandbox.env */; }; + 582C308B2C80B8EC008CF551 /* canary.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12C2C8077B600B319B5 /* canary.env */; }; + 582C308C2C80B8EC008CF551 /* mainnet.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12E2C807B8E00B319B5 /* mainnet.env */; }; + 582C308D2C80B8EC008CF551 /* sandbox.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12F2C807B8E00B319B5 /* sandbox.env */; }; + 582C5F8E2C7654A800B678AE /* ConfigurationManager in Frameworks */ = {isa = PBXBuildFile; productRef = 582C5F8D2C7654A800B678AE /* ConfigurationManager */; }; + 5854E81E2C80BB5A005EE1F4 /* canary.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12C2C8077B600B319B5 /* canary.env */; }; + 5854E81F2C80BB5A005EE1F4 /* mainnet.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12E2C807B8E00B319B5 /* mainnet.env */; }; + 5854E8202C80BB5A005EE1F4 /* sandbox.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12F2C807B8E00B319B5 /* sandbox.env */; }; + 5879C2062C80A37200BAB142 /* TunnelSettingsConversions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5879C2052C80A37200BAB142 /* TunnelSettingsConversions.swift */; }; D91E36A92BF3AA7F00BDE84A /* HelperManager in Frameworks */ = {isa = PBXBuildFile; productRef = D91E36A82BF3AA7F00BDE84A /* HelperManager */; }; D92219E62BA1CF99005EE6AC /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9CF05D82BA1CC7100489055 /* SystemConfiguration.framework */; }; D924A8E62C33F3D400465E84 /* ExternalLinkManager in Frameworks */ = {isa = PBXBuildFile; productRef = D924A8E52C33F3D400465E84 /* ExternalLinkManager */; }; @@ -22,12 +32,6 @@ D944053F2BAC4CBD0076EEDB /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D9CF05D82BA1CC7100489055 /* SystemConfiguration.framework */; }; D94866FF2BE035C300F5331B /* CredentialsManager in Frameworks */ = {isa = PBXBuildFile; productRef = D94866FE2BE035C300F5331B /* CredentialsManager */; }; D94867012BE035FB00F5331B /* Constants in Frameworks */ = {isa = PBXBuildFile; productRef = D94867002BE035FB00F5331B /* Constants */; }; - D94CC12D2C8077B600B319B5 /* canary.env in Sources */ = {isa = PBXBuildFile; fileRef = D94CC12C2C8077B600B319B5 /* canary.env */; }; - D94CC1302C807B8E00B319B5 /* mainnet.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12E2C807B8E00B319B5 /* mainnet.env */; }; - D94CC1312C807B8E00B319B5 /* sandbox.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12F2C807B8E00B319B5 /* sandbox.env */; }; - D94CC1322C807B9300B319B5 /* sandbox.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12F2C807B8E00B319B5 /* sandbox.env */; }; - D94CC1332C807B9300B319B5 /* canary.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12C2C8077B600B319B5 /* canary.env */; }; - D94CC1342C807B9300B319B5 /* mainnet.env in Resources */ = {isa = PBXBuildFile; fileRef = D94CC12E2C807B8E00B319B5 /* mainnet.env */; }; D9538E2E2B7E3F28002FA8B5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D9538E2D2B7E3F28002FA8B5 /* LaunchScreen.storyboard */; }; D954C0B92B51C1CA00C6BAAC /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D954C0A62B51C17200C6BAAC /* NetworkExtension.framework */; }; D954C0BC2B51C1CA00C6BAAC /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D954C0BB2B51C1CA00C6BAAC /* PacketTunnelProvider.swift */; }; @@ -137,6 +141,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 5879C2052C80A37200BAB142 /* TunnelSettingsConversions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelSettingsConversions.swift; sourceTree = ""; }; D90F19042BEED69D008D2232 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; D9264E252B3DA062004594A8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; D9281C462B8884D400834B53 /* NymVPN.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NymVPN.entitlements; sourceTree = ""; }; @@ -193,8 +198,8 @@ D9BB53552BAC4AE500917C97 /* Tunnels in Frameworks */, D93214472B84EBEA00FFDC5E /* Logging in Frameworks */, D954C0B92B51C1CA00C6BAAC /* NetworkExtension.framework in Frameworks */, - 5886CDF52C6CB544001F9651 /* Constants in Frameworks */, D9281C452B877B8800834B53 /* Keychain in Frameworks */, + 582C5F8E2C7654A800B678AE /* ConfigurationManager in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -284,6 +289,7 @@ isa = PBXGroup; children = ( D954C0BB2B51C1CA00C6BAAC /* PacketTunnelProvider.swift */, + 5879C2052C80A37200BAB142 /* TunnelSettingsConversions.swift */, D954C0BD2B51C1CA00C6BAAC /* Info.plist */, D954C0BE2B51C1CA00C6BAAC /* NymMixnetTunnel.entitlements */, ); @@ -417,7 +423,7 @@ D9BB53542BAC4AE500917C97 /* Tunnels */, D99820972BB6E8D9000C1FEE /* MixnetLibrary */, D9309AF82BC81B7600C387F4 /* TunnelMixnet */, - 5886CDF42C6CB544001F9651 /* Constants */, + 582C5F8D2C7654A800B678AE /* ConfigurationManager */, ); productName = NymMixnetTunnel; productReference = D954C0B82B51C1CA00C6BAAC /* NymMixnetTunnel.appex */; @@ -580,6 +586,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5854E81E2C80BB5A005EE1F4 /* canary.env in Resources */, + 5854E81F2C80BB5A005EE1F4 /* mainnet.env in Resources */, + 5854E8202C80BB5A005EE1F4 /* sandbox.env in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -587,10 +596,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 582C30882C80B8E7008CF551 /* canary.env in Resources */, + 582C30892C80B8E7008CF551 /* mainnet.env in Resources */, + 582C308A2C80B8E7008CF551 /* sandbox.env in Resources */, D9538E2E2B7E3F28002FA8B5 /* LaunchScreen.storyboard in Resources */, - D94CC1312C807B8E00B319B5 /* sandbox.env in Resources */, D98B32EE2C58E24E005A15A3 /* Package.resolved in Resources */, - D94CC1302C807B8E00B319B5 /* mainnet.env in Resources */, D96B94E02C2AB62D00E98B15 /* gatewaysExitCountries.json in Resources */, D96B94DE2C2AB62D00E98B15 /* gatewaysEntryCountries.json in Resources */, D9264E262B3DA062004594A8 /* Assets.xcassets in Resources */, @@ -602,13 +612,13 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 582C308B2C80B8EC008CF551 /* canary.env in Resources */, + 582C308C2C80B8EC008CF551 /* mainnet.env in Resources */, + 582C308D2C80B8EC008CF551 /* sandbox.env in Resources */, D9BEF2DA2BEB63320036C566 /* Preview Assets.xcassets in Resources */, D98B32F22C58F682005A15A3 /* Package.resolved in Resources */, D96B94E12C2AB62D00E98B15 /* gatewaysExitCountries.json in Resources */, D96B94DF2C2AB62D00E98B15 /* gatewaysEntryCountries.json in Resources */, - D94CC1322C807B9300B319B5 /* sandbox.env in Resources */, - D94CC1332C807B9300B319B5 /* canary.env in Resources */, - D94CC1342C807B9300B319B5 /* mainnet.env in Resources */, D9BEF2D72BEB63320036C566 /* Assets.xcassets in Resources */, D9F236252C650A9200CCC433 /* LibLicences.json in Resources */, ); @@ -683,6 +693,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5879C2062C80A37200BAB142 /* TunnelSettingsConversions.swift in Sources */, D954C0BC2B51C1CA00C6BAAC /* PacketTunnelProvider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -692,7 +703,6 @@ buildActionMask = 2147483647; files = ( D99A147A2B357E9900F2728B /* NymVPNApp.swift in Sources */, - D94CC12D2C8077B600B319B5 /* canary.env in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1198,9 +1208,9 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 5886CDF42C6CB544001F9651 /* Constants */ = { + 582C5F8D2C7654A800B678AE /* ConfigurationManager */ = { isa = XCSwiftPackageProductDependency; - productName = Constants; + productName = ConfigurationManager; }; D91E36A82BF3AA7F00BDE84A /* HelperManager */ = { isa = XCSwiftPackageProductDependency; diff --git a/nym-vpn-apple/NymVPN/NymVPNApp.swift b/nym-vpn-apple/NymVPN/NymVPNApp.swift index cad8144470..87d2606eb6 100644 --- a/nym-vpn-apple/NymVPN/NymVPNApp.swift +++ b/nym-vpn-apple/NymVPN/NymVPNApp.swift @@ -45,7 +45,7 @@ private extension NymVPNApp { LoggingSystem.bootstrap { label in FileLogHandler(label: label) } - try? ConfigurationManager.setEnvVariables(for: .mainnet) + try? ConfigurationManager.setEnvVariables() ThemeConfiguration.setup() SentryManager.shared.setup() } diff --git a/nym-vpn-apple/NymVPNDaemon/NymVPNDaemonApp.swift b/nym-vpn-apple/NymVPNDaemon/NymVPNDaemonApp.swift index 0e33870049..989e6da012 100644 --- a/nym-vpn-apple/NymVPNDaemon/NymVPNDaemonApp.swift +++ b/nym-vpn-apple/NymVPNDaemon/NymVPNDaemonApp.swift @@ -53,7 +53,7 @@ private extension NymVPNDaemonApp { LoggingSystem.bootstrap { label in FileLogHandler(label: label) } - try? ConfigurationManager.setEnvVariables(for: .mainnet) + try? ConfigurationManager.setEnvVariables() ThemeConfiguration.setup() SentryManager.shared.setup() HelperManager.shared.setup(helperName: Constants.helperName.rawValue) diff --git a/nym-vpn-apple/Scripts/UpdateEnvVars.sh b/nym-vpn-apple/Scripts/UpdateEnvVars.sh index b690f5396a..128d11c49b 100755 --- a/nym-vpn-apple/Scripts/UpdateEnvVars.sh +++ b/nym-vpn-apple/Scripts/UpdateEnvVars.sh @@ -1,8 +1,10 @@ #!/bin/bash -BASE_URL="https://raw.githubusercontent.com/nymtech/nym/develop/envs" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +BASE_URL="https://raw.githubusercontent.com/nymtech/nym/release/2024.10-caramello/envs" FILES=("canary.env" "mainnet.env" "sandbox.env") -DEST_DIR="./Envs" +DEST_DIR=$(realpath "$SCRIPT_DIR/../Envs") for FILE in "${FILES[@]}"; do FILE_URL="$BASE_URL/$FILE" diff --git a/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ConfigurationManager.swift b/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ConfigurationManager.swift index 328134c9a7..77849ab658 100644 --- a/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ConfigurationManager.swift +++ b/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ConfigurationManager.swift @@ -1,30 +1,20 @@ import Foundation import Constants -public enum ConfigurationManager { - public static func setEnvVariables(for environment: Env) throws { - do { - let envString = try ConfigurationManager.contentOfEnvFile(named: environment.rawValue) - try setEnvironmentVariables(envString: envString) - } catch { - print(error) - } +public class ConfigurationManager { + public static func setEnvVariables() throws { + let envString = try ConfigurationManager.contentOfEnvFile(named: Constants.currentEnvironment.rawValue) + try setEnvironmentVariables(envString: envString) } } private extension ConfigurationManager { static func contentOfEnvFile(named: String) throws -> String { - guard let filePath = Bundle.main.path(forResource: named, ofType: "env") - else { + guard let filePath = Bundle.main.path(forResource: named, ofType: "env") else { throw GeneralNymError.noEnvFile } - do { - let fileContent = try String(contentsOfFile: filePath, encoding: .utf8) - print(fileContent) - return fileContent - } catch { - throw error - } + + return try String(contentsOfFile: filePath, encoding: .utf8) } static func setEnvironmentVariables(envString: String) throws { diff --git a/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/Env.swift b/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/Env.swift deleted file mode 100644 index a3bdb8ba7c..0000000000 --- a/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/Env.swift +++ /dev/null @@ -1,5 +0,0 @@ -public enum Env: String { - case canary - case mainnet - case sandbox -} diff --git a/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ParseEnvironmentFileError.swift b/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ParseEnvironmentFileError.swift index 159216440b..a74d5641fc 100644 --- a/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ParseEnvironmentFileError.swift +++ b/nym-vpn-apple/Services/Sources/Services/ConfigurationManager/ParseEnvironmentFileError.swift @@ -16,10 +16,10 @@ public struct ParseEnvironmentFileError: LocalizedError { func errorDescription() -> String? { switch kind { - case .invalidValue: - return "Invalid value" - case .fileNotFound: - return "Env file not found" + case .invalidValue: + return "Invalid value" + case .fileNotFound: + return "Env file not found" } } } diff --git a/nym-vpn-apple/Services/Sources/Services/Constants/Constants.swift b/nym-vpn-apple/Services/Sources/Services/Constants/Constants.swift index ac70240e09..04e7324635 100644 --- a/nym-vpn-apple/Services/Sources/Services/Constants/Constants.swift +++ b/nym-vpn-apple/Services/Sources/Services/Constants/Constants.swift @@ -4,9 +4,6 @@ public enum Constants: String { case groupID = "group.net.nymtech.vpn" case helperName = "net.nymtech.vpn.helper" - case sandboxApiUrl = "https://canary-api.performance.nymte.ch/api" - case sandboxExplorerURL = "https://canary-explorer.performance.nymte.ch/api" - case supportURL = "https://support.nymvpn.com/hc/en-us" case termsOfUseURL = "https://nymvpn.com/en/terms" case privacyPolicyURL = "https://nymvpn.com/en/privacy?type=apps" @@ -17,7 +14,15 @@ public enum Constants: String { case logFileName = "Logs.log" + public static let currentEnvironment: Env = .canary + public static func apiURL() -> URL? { getenv("NYM_API").flatMap { URL(string: String(cString: $0)) } } } + +public enum Env: String { + case canary + case mainnet + case sandbox +} diff --git a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/Config/MixnetConfig.swift b/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/Config/MixnetConfig.swift index 083d600ec7..02858a208c 100644 --- a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/Config/MixnetConfig.swift +++ b/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/Config/MixnetConfig.swift @@ -51,9 +51,8 @@ public struct MixnetConfig: Codable, Equatable { #if os(iOS) // MARK: - VpnConfig - extension MixnetConfig { - public func asVpnConfig(mixnetTunnelProvider: MixnetTunnelProvider) throws -> VpnConfig { - guard let apiURL = Constants.apiURL() - else { + public func asVpnConfig(tunProvider: OsTunProvider, tunStatusListener: TunnelStatusListener?) throws -> VpnConfig { + guard let apiURL = Constants.apiURL() else { throw GeneralNymError.invalidUrl } return VpnConfig( @@ -62,9 +61,9 @@ extension MixnetConfig { entryGateway: entryGateway?.entryPoint ?? .random, exitRouter: exitRouter.exitPoint, enableTwoHop: isTwoHopEnabled, - tunProvider: mixnetTunnelProvider, + tunProvider: tunProvider, credentialDataPath: credentialsDataPath, - tunStatusListener: nil + tunStatusListener: tunStatusListener ) } } diff --git a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetAdapter.swift b/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetAdapter.swift deleted file mode 100644 index cad0b7e669..0000000000 --- a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetAdapter.swift +++ /dev/null @@ -1,47 +0,0 @@ -#if os(iOS) -import Foundation -import Constants -import NetworkExtension -import MixnetLibrary - -public final class MixnetAdapter { - private weak var packetTunnelProvider: NEPacketTunnelProvider? - - public let mixnetTunnelProvider: MixnetTunnelProvider - - public var tunnelFileDescriptor: Int32? { - var buf = [CChar](repeating: 0, count: Int(IFNAMSIZ)) - for fd: Int32 in 0...1024 { - var len = socklen_t(buf.count) - if getsockopt(fd, 2, 2, &buf, &len) == 0 && String(cString: buf).hasPrefix("utun") { - return fd - } - } - return nil - } - - public init( - with packetTunnelProvider: NEPacketTunnelProvider, - mixnetTunnelProvider: MixnetTunnelProvider - ) { - self.packetTunnelProvider = packetTunnelProvider - self.mixnetTunnelProvider = mixnetTunnelProvider - } - - public func start(with vpnConfig: VpnConfig) throws { - do { - try runVpn(config: vpnConfig) - } catch let error { - throw error - } - } - - public func stop() throws { - do { - try stopVpn() - } catch let error { - throw error - } - } -} -#endif diff --git a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetTunnelProvider.swift b/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetTunnelProvider.swift deleted file mode 100644 index aba6aeda1d..0000000000 --- a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetTunnelProvider.swift +++ /dev/null @@ -1,77 +0,0 @@ -#if os(iOS) -import Foundation -import MixnetLibrary - -public final class MixnetTunnelProvider: OsTunProvider { - public var nymOnConfigure: (() -> Void)? - public var fileDescriptor: Int32? - public var nymConfig: NymConfig? - - public init(nymOnConfigure: (() -> Void)? = nil, fileDescriptor: Int32? = nil) { - self.nymOnConfigure = nymOnConfigure - self.fileDescriptor = fileDescriptor - } - - public func configureNym(config: NymConfig) throws -> Int32 { - let semaphore = DispatchSemaphore(value: 0) - nymConfig = config - - guard let nymOnConfigure else { throw FfiError.FdNotFound } - - nymOnConfigure() - semaphore.signal() - - semaphore.wait() - - guard let fileDescriptor else { throw FfiError.FdNotFound } - return Int32(fileDescriptor) - } - - public func configureWg(config: WgConfig) throws { -// let newConfiguration = tunnelConfiguration(from: config) -// print(tunnelConfiguration) -// appSettings.tunnelConfiguration = newConfiguration - } -} - -// private extension MixnetTunnelProvider { -// func tunnelConfiguration(from config: WgConfig) -> TunnelConfiguration? { -// guard let privateKey = WireGuardKit.PrivateKey(base64Key: config.tunnel.privateKey) -// else { -// return nil -// } -// -// var interfaceConfiguration = InterfaceConfiguration(privateKey: privateKey) -// if let ipv6Gateway = config.ipv6Gateway, let ipv6AddressRange = IPAddressRange(from: ipv6Gateway) { -// interfaceConfiguration.addresses.append(ipv6AddressRange) -// } -// interfaceConfiguration.addresses = [ -// IPAddressRange(from: config.ipv4Gateway) -// ].compactMap { $0 } -// -// let peers = config.peers.compactMap { configuration -> PeerConfiguration? in -// guard -// let publicKey = WireGuardKit.PublicKey(base64Key: configuration.publicKey), -// let psk = configuration.psk, -// let presharedKey = WireGuardKit.PreSharedKey(base64Key: psk) -// else { -// return nil -// } -// -// var peerConfiguration = WireGuardKit.PeerConfiguration(publicKey: publicKey) -// peerConfiguration.preSharedKey = presharedKey -// peerConfiguration.allowedIPs = configuration.allowedIps.compactMap { -// IPAddressRange(from: $0) -// } -// peerConfiguration.endpoint = Endpoint(from: configuration.endpoint) -// return peerConfiguration -// } -// -// return TunnelConfiguration( -// name: "NymVPN 2 Hop", -// interface: interfaceConfiguration, -// peers: peers -// ) -// } -// } -#endif diff --git a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetTunnelSettingsGenerator.swift b/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetTunnelSettingsGenerator.swift deleted file mode 100644 index 139b56cbd9..0000000000 --- a/nym-vpn-apple/Services/Sources/Services/TunnelMixnet/MixnetTunnelSettingsGenerator.swift +++ /dev/null @@ -1,170 +0,0 @@ -#if os(iOS) -import NetworkExtension -import MixnetLibrary - -public final class MixnetTunnelSettingsGenerator { - private let nymConfig: NymConfig - - public init(nymConfig: NymConfig) { - self.nymConfig = nymConfig - } - - public func generateNetworkSettings() -> NEPacketTunnelNetworkSettings { - // iOS requires a tunnel endpoint, whereas in WireGuard it's valid for - // a tunnel to have no endpoint, or for there to be many endpoints, in - // which case, displaying a single one in settings doesn't really - // make sense. So, we fill it in with this placeholder, which is not - // a valid IP address that will actually route over the Internet. - let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1") - - let mtu = nymConfig.mtu - - // 0 means automatic MTU. In theory, we should just do - // `networkSettings.tunnelOverheadBytes = 80` but in - // practice there are too many broken networks out there. - // Instead set it to 1280. Boohoo. Maybe someday we'll - // add a nob, maybe, or iOS will do probing for us. - if mtu == 0 { - #if os(iOS) - networkSettings.mtu = NSNumber(value: 1280) - #elseif os(macOS) - networkSettings.tunnelOverheadBytes = 80 - #else - #error("Unimplemented") - #endif - } else { - networkSettings.mtu = NSNumber(value: mtu) - } - - let (ipv4Addresses, ipv6Addresses) = addresses() - let (ipv4IncludedRoutes, ipv6IncludedRoutes) = includedRoutes() - let (ipv4ExcludedRoutes, ipv6ExcludedRoutes) = excludedRoutes() - - let ipv4Settings = NEIPv4Settings( - addresses: ipv4Addresses.map { $0.destinationAddress }, - subnetMasks: ipv4Addresses.map { $0.destinationSubnetMask } - ) - ipv4Settings.includedRoutes = ipv4IncludedRoutes - ipv4Settings.excludedRoutes = ipv4ExcludedRoutes - networkSettings.ipv4Settings = ipv4Settings - - let ipv6Settings = NEIPv6Settings( - addresses: ipv6Addresses.map { $0.destinationAddress }, - networkPrefixLengths: ipv6Addresses.map { $0.destinationNetworkPrefixLength } - ) - ipv6Settings.includedRoutes = ipv6IncludedRoutes - ipv6Settings.excludedRoutes = ipv6ExcludedRoutes - networkSettings.ipv6Settings = ipv6Settings - - // TODO: expose DNS settings in uniffy layer and set the values here - networkSettings.dnsSettings = NEDNSSettings(servers: ["1.1.1.1", "1.0.0.1"]) - - return networkSettings - } - - private func addresses() -> ([NEIPv4Route], [NEIPv6Route]) { - var ipv4Routes = [NEIPv4Route]() - var ipv6Routes = [NEIPv6Route]() - let ipv4AddressRange = IPAddressRange(from: nymConfig.ipv4Addr) - let ipv6AddressRange = IPAddressRange(from: nymConfig.ipv6Addr) - - if let addressRange = ipv4AddressRange, addressRange.address is IPv4Address { - ipv4Routes.append( - NEIPv4Route( - destinationAddress: "\(addressRange.address)", - subnetMask: "\(addressRange.subnetMask())" - ) - ) - } - - if let addressRange = ipv6AddressRange, addressRange.address is IPv6Address { - // Big fat ugly hack for broken iOS networking stack: the smallest prefix that will have - // any effect on iOS is a /120, so we clamp everything above to /120. This is potentially - // very bad, if various network parameters were actually relying on that subnet being - // intentionally small. TODO: talk about this with upstream iOS devs. - ipv6Routes.append(NEIPv6Route(destinationAddress: "\(addressRange.address)", networkPrefixLength: NSNumber(value: min(120, addressRange.networkPrefixLength)))) - } - return (ipv4Routes, ipv6Routes) - } - - private func includedRoutes() -> ([NEIPv4Route], [NEIPv6Route]) { - var ipv4IncludedRoutes = [NEIPv4Route]() - var ipv6IncludedRoutes = [NEIPv6Route]() - - let ipv4AddressRange = IPAddressRange(from: nymConfig.ipv4Addr) - let ipv6AddressRange = IPAddressRange(from: nymConfig.ipv6Addr) - - let ipv4includedRouteRange = IPAddressRange(from: "0.0.0.0/0") - let ipv6includedRouteRange = IPAddressRange(from: "::/0") - - if let addressRange = ipv4AddressRange, addressRange.address is IPv4Address { - let route = NEIPv4Route( - destinationAddress: "\(addressRange.maskedAddress())", - subnetMask: "\(addressRange.subnetMask())" - ) - route.gatewayAddress = "\(addressRange.address)" - ipv4IncludedRoutes.append(route) - } - - if let addressRange = ipv6AddressRange, addressRange.address is IPv6Address { - let route = NEIPv6Route( - destinationAddress: "\(addressRange.maskedAddress())", - networkPrefixLength: NSNumber(value: addressRange.networkPrefixLength) - ) - route.gatewayAddress = "\(addressRange.address)" - ipv6IncludedRoutes.append(route) - } - - if let addressRange = ipv4includedRouteRange { - ipv4IncludedRoutes.append( - NEIPv4Route( - destinationAddress: "\(addressRange.address)", - subnetMask: "\(addressRange.subnetMask())" - ) - ) - } - - if let addressRange = ipv6includedRouteRange { - ipv6IncludedRoutes.append( - NEIPv6Route( - destinationAddress: "\(addressRange.address)", - networkPrefixLength: NSNumber(value: addressRange.networkPrefixLength) - ) - ) - } - - return (ipv4IncludedRoutes, ipv6IncludedRoutes) - } - - private func excludedRoutes() -> ([NEIPv4Route], [NEIPv6Route]) { - var ipv4ExcludedRoutes = [NEIPv4Route]() - var ipv6ExcludedRoutes = [NEIPv6Route]() - - guard - let entryMixnetGatewayIp = nymConfig.entryMixnetGatewayIp, - let addressRange = IPAddressRange(from: entryMixnetGatewayIp) - else { - return (ipv4ExcludedRoutes, ipv6ExcludedRoutes) - } - - if addressRange.address is IPv4Address { - ipv4ExcludedRoutes.append( - NEIPv4Route( - destinationAddress: "\(addressRange.address)", - subnetMask: "\(addressRange.subnetMask())" - ) - ) - } - - if addressRange.address is IPv6Address { - ipv6ExcludedRoutes.append( - NEIPv6Route( - destinationAddress: "\(addressRange.address)", - networkPrefixLength: NSNumber(value: addressRange.networkPrefixLength) - ) - ) - } - return (ipv4ExcludedRoutes, ipv6ExcludedRoutes) - } -} -#endif diff --git a/nym-vpn-apple/Settings/Sources/Settings/Logs/LogsView.swift b/nym-vpn-apple/Settings/Sources/Settings/Logs/LogsView.swift index 7984c93105..9fa26c28d5 100644 --- a/nym-vpn-apple/Settings/Sources/Settings/Logs/LogsView.swift +++ b/nym-vpn-apple/Settings/Sources/Settings/Logs/LogsView.swift @@ -104,7 +104,7 @@ private extension LogsView { } .disabled(viewModel.logs.isEmpty) .simultaneousGesture( - TapGesture().onEnded({ generateImpact() }) + TapGesture().onEnded { generateImpact() } ) #endif #if os(macOS) diff --git a/nym-vpn-core/Cargo.lock b/nym-vpn-core/Cargo.lock index 59205eed33..a0307f5636 100644 --- a/nym-vpn-core/Cargo.lock +++ b/nym-vpn-core/Cargo.lock @@ -824,12 +824,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -2044,6 +2038,18 @@ version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.59.0", +] + [[package]] name = "finl_unicode" version = "1.2.0" @@ -2118,6 +2124,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "funty" version = "2.0.0" @@ -2392,9 +2407,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ "atomic-waker", "bytes", @@ -2712,7 +2727,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", + "h2 0.4.6", "http 1.1.0", "http-body 1.0.0", "httparse", @@ -2874,6 +2889,17 @@ dependencies = [ "hashbrown 0.14.3", ] +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags 1.3.2", + "inotify-sys", + "libc", +] + [[package]] name = "inotify" version = "0.10.2" @@ -3107,6 +3133,26 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33070833c9ee02266356de0c43f723152bd38bd96ddf52c82b3af10c9138b28" +[[package]] +name = "kqueue" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7447f1ca1b7b563588a205fe93dea8df60fd981423a768bc1c0ded35ed147d0c" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -3135,7 +3181,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -3152,6 +3198,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.5.0", "libc", + "redox_syscall 0.5.3", ] [[package]] @@ -3266,9 +3313,9 @@ dependencies = [ [[package]] name = "md5-asm" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61d33bc4cdfe5c60340e282bbbee0a6e2bc57f0b9279bb3489c5004d12492e5c" +checksum = "d19b8ee7fc7d812058d3b708c7f719efd0713d53854648e4223c6fcae709e2df" dependencies = [ "cc", ] @@ -3294,6 +3341,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.17" @@ -3325,6 +3381,18 @@ dependencies = [ "adler", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.0.1" @@ -3514,7 +3582,7 @@ dependencies = [ "cc", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -3528,18 +3596,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "cfg_aliases 0.1.1", - "libc", -] - [[package]] name = "nix" version = "0.29.0" @@ -3548,8 +3604,9 @@ checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.5.0", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", + "memoffset 0.9.1", ] [[package]] @@ -3568,6 +3625,24 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "notify" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729f63e1ca555a43fe3efa4f3efdf4801c479da85b432242a7b726f353c88486" +dependencies = [ + "bitflags 1.3.2", + "crossbeam-channel", + "filetime", + "fsevent-sys", + "inotify 0.9.6", + "kqueue", + "libc", + "mio 0.8.11", + "walkdir", + "windows-sys 0.45.0", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -5247,6 +5322,7 @@ version = "0.1.17-dev" dependencies = [ "android_logger", "anyhow", + "async-trait", "bincode", "bs58 0.5.1", "bytes", @@ -5261,7 +5337,7 @@ dependencies = [ "log", "netdev", "nix 0.23.2", - "nix 0.28.0", + "nix 0.29.0", "nym-authenticator-client", "nym-authenticator-requests", "nym-bandwidth-controller 0.1.0 (git+https://github.com/nymtech/nym?rev=3f922cc0)", @@ -5292,6 +5368,8 @@ dependencies = [ "rand 0.8.5", "serde", "serde_json", + "shadowsocks", + "shadowsocks-service", "signature 2.2.0", "sqlx", "talpid-core", @@ -6258,6 +6336,15 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" version = "0.4.5" @@ -6945,9 +7032,9 @@ dependencies = [ [[package]] name = "sha1-asm" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ba6947745e7f86be3b8af00b7355857085dbdf8901393c89514510eb61f4e21" +checksum = "286acebaf8b67c1130aedffad26f594eff0c1292389158135327d2e23aed582b" dependencies = [ "cc", ] @@ -6992,6 +7079,7 @@ version = "1.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f16fc99441d1a1b91b5f7b5093773d631ad506761a973e0a632f1836d1b05939" dependencies = [ + "arc-swap", "async-trait", "base64 0.13.1", "byte_string", @@ -7001,6 +7089,7 @@ dependencies = [ "libc", "log", "nix 0.23.2", + "notify", "once_cell", "pin-project", "sendfd", @@ -7012,6 +7101,7 @@ dependencies = [ "thiserror", "tokio", "tokio-tfo", + "trust-dns-resolver", "url", "winapi", ] @@ -7499,7 +7589,7 @@ dependencies = [ [[package]] name = "talpid-core" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "async-trait", "atty", @@ -7511,14 +7601,14 @@ dependencies = [ "err-derive", "futures", "hex", - "inotify", + "inotify 0.10.2", "internet-checksum", "ipnetwork", "jnix", "lazy_static", "libc", "log", - "memoffset", + "memoffset 0.6.5", "mnl", "netlink-packet-route 0.13.0", "netlink-sys", @@ -7566,7 +7656,7 @@ dependencies = [ [[package]] name = "talpid-dbus" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "dbus", "err-derive", @@ -7579,7 +7669,7 @@ dependencies = [ [[package]] name = "talpid-openvpn" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "async-trait", "atty", @@ -7616,7 +7706,7 @@ dependencies = [ [[package]] name = "talpid-platform-metadata" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "rs-release", "talpid-dbus", @@ -7626,7 +7716,7 @@ dependencies = [ [[package]] name = "talpid-routing" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "err-derive", "futures", @@ -7649,7 +7739,7 @@ dependencies = [ [[package]] name = "talpid-time" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "libc", "tokio", @@ -7658,7 +7748,7 @@ dependencies = [ [[package]] name = "talpid-tunnel" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "cfg-if", "duct", @@ -7679,7 +7769,7 @@ dependencies = [ [[package]] name = "talpid-tunnel-config-client" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "classic-mceliece-rust", "log", @@ -7695,7 +7785,7 @@ dependencies = [ [[package]] name = "talpid-types" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "base64 0.13.1", "err-derive", @@ -7710,7 +7800,7 @@ dependencies = [ [[package]] name = "talpid-windows-net" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "err-derive", "futures", @@ -7723,7 +7813,7 @@ dependencies = [ [[package]] name = "talpid-wireguard" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "bitflags 1.3.2", "byteorder", @@ -8075,7 +8165,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.1", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", @@ -8724,7 +8814,7 @@ dependencies = [ [[package]] name = "tunnel-obfuscation" version = "0.0.0" -source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=5f478c385#5f478c3856a1158fa9598b9c77d4093aeadcfd01" +source = "git+https://github.com/nymtech/nym-vpn-mullvad-libs?rev=f9fd4be7f#f9fd4be7f7d2687874b95e54cc89838c402946bc" dependencies = [ "async-trait", "err-derive", @@ -9347,6 +9437,15 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -9374,6 +9473,21 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.48.5" diff --git a/nym-vpn-core/crates/nym-wg-gateway-client/Cargo.toml b/nym-vpn-core/crates/nym-wg-gateway-client/Cargo.toml index 4ab927f1b5..f1596d2387 100644 --- a/nym-vpn-core/crates/nym-wg-gateway-client/Cargo.toml +++ b/nym-vpn-core/crates/nym-wg-gateway-client/Cargo.toml @@ -25,4 +25,4 @@ nym-authenticator-client = { path = "../nym-authenticator-client" } nym-gateway-directory = { path = "../nym-gateway-directory" } # Only a very weak dependency and something that we should need (soon) -talpid-types = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "5f478c385" } +talpid-types = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "f9fd4be7f" } diff --git a/nym-vpn-core/crates/nym-wg-go/build.rs b/nym-vpn-core/crates/nym-wg-go/build.rs new file mode 100644 index 0000000000..98a31d2be7 --- /dev/null +++ b/nym-vpn-core/crates/nym-wg-go/build.rs @@ -0,0 +1,39 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::{env, path::PathBuf}; + +fn main() { + let manifest_path = env::var_os("CARGO_MANIFEST_DIR").expect("manifest dir is not set"); + let target = env::var("TARGET").expect("target is not set"); + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("target os is not set"); + + let mut build_dir = PathBuf::from(manifest_path) + .join("../../../build/lib") + .canonicalize() + .expect("failed to canonicalize build dir path"); + + build_dir.push(target); + + // CI may only provide universal builds + if target_os == "macos" { + let target_dir_exists = build_dir + .try_exists() + .expect("failed to check existence of target dir"); + + if !target_dir_exists { + build_dir.pop(); + build_dir.push("universal-apple-darwin"); + } + } + + println!("cargo::rustc-link-search={}", build_dir.display()); + + let link_type = match target_os.as_str() { + "android" => "", + "linux" | "macos" | "ios" => "=static", + "windows" => "dylib", + _ => panic!("Unsupported platform: {}", target_os), + }; + println!("cargo:rustc-link-lib{}=wg", link_type); +} diff --git a/nym-vpn-core/nym-vpn-lib/Cargo.toml b/nym-vpn-core/nym-vpn-lib/Cargo.toml index 154d7cf6a3..8c380b9b02 100644 --- a/nym-vpn-core/nym-vpn-lib/Cargo.toml +++ b/nym-vpn-core/nym-vpn-lib/Cargo.toml @@ -17,6 +17,7 @@ path = "uniffi-bindgen.rs" [dependencies] anyhow.workspace = true +async-trait.workspace = true bincode.workspace = true bs58.workspace = true bytes.workspace = true @@ -50,12 +51,16 @@ tun2 = { workspace = true, features = ["async"] } uniffi = { workspace = true, features = ["cli"] } url.workspace = true -talpid-core = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "5f478c385" } -talpid-platform-metadata = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "5f478c385" } -talpid-routing = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "5f478c385" } -talpid-tunnel = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "5f478c385" } -talpid-types = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "5f478c385" } -talpid-wireguard = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "5f478c385" } +# Pin down shadowsocks to prevent mullvad dependencies from using newer crates that aren't compatible. +shadowsocks-service = { version = "=1.14.3" } +shadowsocks = { version = "=1.14.2" } + +talpid-core = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "f9fd4be7f" } +talpid-platform-metadata = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "f9fd4be7f" } +talpid-routing = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "f9fd4be7f" } +talpid-tunnel = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "f9fd4be7f" } +talpid-types = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "f9fd4be7f" } +talpid-wireguard = { git = "https://github.com/nymtech/nym-vpn-mullvad-libs", rev = "f9fd4be7f" } nym-authenticator-requests.workspace = true nym-bandwidth-controller-pre-ecash.workspace = true @@ -95,13 +100,16 @@ rand = "0.8.5" oslog = "0.2.0" [target.'cfg(unix)'.dependencies] -nix = { version = "0.28.0", features = ["user"] } +nix = { workspace = true, features = ["user"] } + +[target.'cfg(target_os = "ios")'.dependencies] +nix = { workspace = true, features = ["socket", "net"] } [target.'cfg(windows)'.dependencies] is_elevated = "0.1.2" [build-dependencies] -uniffi = { version = "0.27.3", features = ["build"] } +uniffi = { workspace = true, features = ["build"] } vergen = { workspace = true, default-features = false, features = [ "build", "git", diff --git a/nym-vpn-core/nym-vpn-lib/src/error.rs b/nym-vpn-core/nym-vpn-lib/src/error.rs index e5f8e326f2..a1de5f79ec 100644 --- a/nym-vpn-core/nym-vpn-lib/src/error.rs +++ b/nym-vpn-core/nym-vpn-lib/src/error.rs @@ -120,6 +120,10 @@ pub enum Error { #[error("failed to parse entry gateway ipv4: {0}")] FailedToParseEntryGatewayIpv4(#[source] std::net::AddrParseError), + + #[cfg(target_os = "ios")] + #[error("failed to run wireguard tunnel")] + RunTunnel(#[from] crate::mobile::Error), } #[derive(thiserror::Error, Debug)] diff --git a/nym-vpn-core/nym-vpn-lib/src/lib.rs b/nym-vpn-core/nym-vpn-lib/src/lib.rs index 374c869fc2..cf4bf373cd 100644 --- a/nym-vpn-core/nym-vpn-lib/src/lib.rs +++ b/nym-vpn-core/nym-vpn-lib/src/lib.rs @@ -10,6 +10,8 @@ pub mod util; mod bandwidth_controller; mod error; mod mixnet; +#[cfg(any(target_os = "ios", target_os = "android"))] +mod mobile; mod platform; mod routing; mod tunnel; @@ -19,6 +21,8 @@ mod vpn; mod wireguard_config; mod wireguard_setup; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + // Re-export some our nym dependencies pub use nym_config; pub use nym_connection_monitor as connection_monitor; @@ -47,3 +51,10 @@ pub use crate::{ SpecificVpn, }, }; + +pub const DEFAULT_DNS_SERVERS: [IpAddr; 4] = [ + IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), + IpAddr::V4(Ipv4Addr::new(1, 0, 0, 1)), + IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)), + IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001)), +]; diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/ios/default_path_observer.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/default_path_observer.rs new file mode 100644 index 0000000000..f86548961e --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/default_path_observer.rs @@ -0,0 +1,64 @@ +use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender}; + +/// The type alias for the receiving end of path observer. +pub type DefaultPathReceiver = UnboundedReceiver; + +/// Observer type that wraps network path changes into a channel. +#[derive(Debug)] +pub struct DefaultPathObserver { + tx: UnboundedSender, +} + +impl DefaultPathObserver { + pub fn new(tx: UnboundedSender) -> Self { + Self { tx } + } +} + +impl OSDefaultPathObserver for DefaultPathObserver { + fn on_default_path_change(&self, new_path: OSDefaultPath) { + if self.tx.send(new_path).is_err() { + tracing::warn!("Failed to send default path change."); + } + } +} + +#[derive(uniffi::Enum, Debug)] +pub enum OSPathStatus { + /// The path cannot be evaluated. + Invalid, + + /// The path is ready to be used for network connections. + Satisfied, + + /// The path for network connections is not available, either due to lack of network + /// connectivity or being prohibited by system policy. + Unsatisfied, + + /// The path is not currently satisfied, but may become satisfied upon a connection attempt. + /// This can be due to a service, such as a VPN or a cellular data connection not being activated. + Satisfiable, + + /// Unknown path status was received. + /// The raw variant code is contained in associated value. + Unknown(i64), +} + +/// Represents a default network route used by the system. +#[derive(uniffi::Record, Debug)] +pub struct OSDefaultPath { + /// Indicates whether the process is able to make connection through the given path. + pub status: OSPathStatus, + + /// Set to true for interfaces that are considered expensive, such as when using cellular data plan. + pub is_expensive: bool, + + /// Set to true when using a constrained interface, such as when using low-data mode. + pub is_constrained: bool, +} + +/// Types observing network changes. +#[uniffi::export(with_foreign)] +pub trait OSDefaultPathObserver: Send + Sync + std::fmt::Debug { + fn on_default_path_change(&self, new_path: OSDefaultPath); +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/ios/dns64.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/dns64.rs new file mode 100644 index 0000000000..4afc86de98 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/dns64.rs @@ -0,0 +1,111 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + ffi::CString, + net::{SocketAddr, SocketAddrV4, SocketAddrV6}, +}; + +use nix::{ + libc, + sys::socket::{SockaddrIn, SockaddrIn6, SockaddrLike}, +}; + +use super::super::wg_config::WgPeer; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("Failed to resolve {} (error code: {})", addr, code)] + DnsLookup { code: i32, addr: SocketAddr }, + + #[error("DNS lookup has seemingly succeeded without any results")] + EmptyDnsLookupResult, + + #[error("Failed to convert port number to string")] + PortToString, + + #[error("Failed to convert IP address to string")] + IpAddressToString, +} + +pub type Result = std::result::Result; + +/// Types implementing dns64 resolution. +pub trait Dns64Resolution: Sized { + /// Replace the peer endpoint after re-resolving it with dns64. + fn resolve_in_place(&mut self) -> Result<()>; + + /// Returns a new peer with dns64 re-resolved peer endpoint. + fn resolved(&self) -> Result; +} + +impl Dns64Resolution for WgPeer { + fn resolve_in_place(&mut self) -> Result<()> { + self.endpoint = reresolve_endpoint(self.endpoint)?; + Ok(()) + } + + fn resolved(&self) -> Result { + Ok(WgPeer { + endpoint: reresolve_endpoint(self.endpoint)?, + public_key: self.public_key.clone(), + }) + } +} + +/// Re-resolve an endpoint with dns64 +fn reresolve_endpoint(endpoint: SocketAddr) -> Result { + reresolve_addr(endpoint).inspect(|resolved_endpoint| { + if resolved_endpoint == &endpoint { + tracing::info!("Resolved {} to self", endpoint); + } else { + tracing::info!("Resolved {} to {}", endpoint, resolved_endpoint); + } + }) +} + +/// Returns the new socket address re-resolved with dns64. +/// This should produce an IPv4-mapped IPv6 address usable in IPv6 only networks +/// when connecting to IPv4-only host. +fn reresolve_addr(socket_addr: SocketAddr) -> Result { + let mut hints: libc::addrinfo = unsafe { std::mem::zeroed() }; + hints.ai_flags = 0; // Set to zero to resolve using dns64 + hints.ai_family = libc::AF_UNSPEC; + hints.ai_socktype = libc::SOCK_DGRAM; + hints.ai_protocol = libc::IPPROTO_UDP; + + let node = CString::new(socket_addr.ip().to_string()).map_err(|_| Error::IpAddressToString)?; + let service = CString::new(socket_addr.port().to_string()).map_err(|_| Error::PortToString)?; + + let mut result = std::ptr::null_mut(); + + let err_code = + unsafe { libc::getaddrinfo(node.as_ptr(), service.as_ptr(), &hints, &mut result) }; + if err_code != 0 { + return Err(Error::DnsLookup { + code: err_code, + addr: socket_addr, + }); + } + + if result.is_null() { + return Err(Error::EmptyDnsLookupResult); + }; + + let addr_info = unsafe { *result }; + let resolved_sockaddr = match addr_info.ai_family { + libc::AF_INET => { + unsafe { SockaddrIn::from_raw(addr_info.ai_addr, Some(addr_info.ai_addrlen)) } + .map(|sin| SocketAddr::V4(SocketAddrV4::from(sin))) + } + libc::AF_INET6 => { + unsafe { SockaddrIn6::from_raw(addr_info.ai_addr, Some(addr_info.ai_addrlen)) } + .map(|sin6| SocketAddr::V6(SocketAddrV6::from(sin6))) + } + _ => None, + }; + + unsafe { libc::freeaddrinfo(result) }; + + resolved_sockaddr.ok_or(Error::EmptyDnsLookupResult) +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/ios/mod.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/mod.rs new file mode 100644 index 0000000000..80987ca9a3 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/mod.rs @@ -0,0 +1,4 @@ +pub mod default_path_observer; +pub mod dns64; +pub mod tun; +pub mod tun_provider; diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/ios/tun.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/tun.rs new file mode 100644 index 0000000000..813c012f93 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/tun.rs @@ -0,0 +1,63 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +use std::{ + ffi::{c_char, c_ulong, CStr}, + os::fd::{BorrowedFd, RawFd}, +}; + +use nix::{ + libc::{self, sockaddr, sockaddr_ctl, socklen_t, AF_SYSTEM}, + sys::socket, +}; + +const UTUN_CTL_NAME: &CStr = c"com.apple.net.utun_control"; +const CTLIOCGINFO: c_ulong = 0xc0644e03; + +#[repr(C)] +#[allow(non_camel_case_types)] +struct ctl_info { + ctl_id: u32, + ctl_name: [c_char; 96], +} + +/// Returns tunnel file descriptor on iOS by incrementally probing first 1024 file descriptors. +pub fn get_tun_fd() -> Option { + let mut ctl_info: ctl_info = unsafe { std::mem::zeroed() }; + unsafe { + std::ptr::copy_nonoverlapping( + UTUN_CTL_NAME.as_ptr(), + ctl_info.ctl_name.as_mut_ptr(), + UTUN_CTL_NAME.count_bytes(), + ) + }; + + (0..1024).find(|fd| { + let mut ctl_addr: sockaddr_ctl = unsafe { std::mem::zeroed() }; + let mut len = std::mem::size_of_val(&ctl_addr) as socklen_t; + let mut ret = unsafe { + libc::getpeername( + *fd, + &mut ctl_addr as *mut sockaddr_ctl as *mut sockaddr, + &mut len, + ) + }; + + if ret == 0 && ctl_addr.sc_family as i32 == AF_SYSTEM { + ret = unsafe { libc::ioctl(*fd, CTLIOCGINFO, &mut ctl_info) }; + ret == 0 && ctl_addr.sc_id == ctl_info.ctl_id + } else { + false + } + }) +} + +/// Returns tunnel interface name +pub fn get_tun_ifname(tun_fd: RawFd) -> Option { + let borrowed_fd = unsafe { BorrowedFd::borrow_raw(tun_fd) }; + + socket::getsockopt(&borrowed_fd, socket::sockopt::UtunIfname) + .ok()? + .into_string() + .ok() +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/ios/tun_provider.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/tun_provider.rs new file mode 100644 index 0000000000..e87eb4be08 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/ios/tun_provider.rs @@ -0,0 +1,22 @@ +use std::sync::Arc; + +use super::{ + super::tunnel_settings::TunnelNetworkSettings, default_path_observer::OSDefaultPathObserver, +}; +use crate::platform::error::FFIError; + +#[uniffi::export(with_foreign)] +#[async_trait::async_trait] +pub trait OSTunProvider: Send + Sync + std::fmt::Debug { + /// Set network settings including tun, dns, ip. + async fn set_tunnel_network_settings( + &self, + tunnel_settings: TunnelNetworkSettings, + ) -> std::result::Result<(), FFIError>; + + /// Set or unset the default path observer. + fn set_default_path_observer( + &self, + observer: Option>, + ) -> std::result::Result<(), FFIError>; +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/mod.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/mod.rs new file mode 100644 index 0000000000..46adea108b --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/mod.rs @@ -0,0 +1,40 @@ +// Copyright 2024 - Nym Technologies SA +// SPDX-License-Identifier: Apache-2.0 + +//! WireGuard tunnel creation and management on Android and iOS + +#[cfg(target_os = "ios")] +pub mod ios; +#[cfg(target_os = "ios")] +pub mod runner; +pub mod tunnel_settings; +pub mod two_hop_config; +pub mod two_hop_tunnel; +pub mod wg_config; + +use crate::platform::error::FFIError; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("failed to locate tun fd")] + CannotLocateTunFd, + + #[error("failed to obtain tun interface name")] + ObtainTunName, + + #[error("tunnel failure")] + Tunnel(#[from] nym_wg_go::Error), + + #[cfg(target_os = "ios")] + #[error("DNS resolution failure")] + DnsResolution(#[from] ios::dns64::Error), + + #[error("failed to set network settings")] + SetNetworkSettings(#[source] FFIError), + + #[cfg(target_os = "ios")] + #[error("failed to set default path observer")] + SetDefaultPathObserver(#[source] FFIError), +} + +pub type Result = std::result::Result; diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/runner.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/runner.rs new file mode 100644 index 0000000000..77fa09ed92 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/runner.rs @@ -0,0 +1,427 @@ +use std::net::IpAddr; +use std::sync::Arc; +use std::time::Duration; + +use ipnetwork::{IpNetwork, Ipv4Network}; +use tokio::task::JoinSet; +use tokio_util::sync::CancellationToken; + +use nym_authenticator_client::AuthClient; +use nym_gateway_directory::{ + AuthAddresses, EntryPoint, ExitPoint, Gateway, GatewayClient, Recipient, +}; +use nym_sdk::UserAgent; +use nym_task::TaskManager; +use nym_wg_gateway_client::{GatewayData, WgGatewayClient}; +use nym_wg_go::{PrivateKey, PublicKey}; + +use crate::mixnet::SharedMixnetClient; +use crate::platform::VPNConfig; +use crate::{bandwidth_controller::BandwidthController, GenericNymVpnConfig}; +use crate::{GatewayDirectoryError, MixnetClientConfig}; + +use super::ios::tun_provider::OSTunProvider; +use super::{ + two_hop_tunnel::TwoHopTunnel, + wg_config::{WgInterface, WgNodeConfig, WgPeer}, +}; + +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("mixnet timed out during startup")] + StartMixnetTimeout, + + #[error("failed to start mixnet client: {0}")] + StartMixnetClient(#[source] Box), + + #[error("gateway directory error: {0}")] + GatewayDirectory(#[from] GatewayDirectoryError), + + #[error("authenticator address not found")] + AuthenticatorAddressNotFound, + + #[error("not enough bandwidth")] + NotEnoughBandwidth, + + #[error("wirewireguardurad authentication is not possible due to one of the gateways not running the authenticator process: {0}")] + AuthenticationNotPossible(String), + + #[error("wireguard gateway failure: {0}")] + WgGatewayClientFailure(#[from] nym_wg_gateway_client::Error), + + #[error("failed to run two hop tunnel: {0}")] + Tunnel(#[source] super::Error), +} + +pub type Result = std::result::Result; + +const MIXNET_CLIENT_STARTUP_TIMEOUT_SECS: Duration = Duration::from_secs(30); +const TASK_MANAGER_SHUTDOWN_TIMER_SECS: u64 = 10; + +pub struct WgTunnelRunner { + gateway_directory_client: GatewayClient, + task_manager: TaskManager, + generic_config: GenericNymVpnConfig, + #[cfg(target_os = "ios")] + tun_provider: Arc, + shutdown_token: CancellationToken, +} + +impl WgTunnelRunner { + pub fn new(config: VPNConfig, shutdown_token: CancellationToken) -> Result { + let user_agent = UserAgent::from(nym_bin_common::bin_info_local_vergen!()); + tracing::info!("User agent: {user_agent}"); + + let generic_config = GenericNymVpnConfig { + mixnet_client_config: MixnetClientConfig { + enable_poisson_rate: false, + disable_background_cover_traffic: false, + enable_credentials_mode: false, + min_mixnode_performance: None, + min_gateway_performance: None, + }, + data_path: config.credential_data_path, + gateway_config: nym_gateway_directory::Config { + api_url: config.api_url, + nym_vpn_api_url: config.vpn_api_url, + min_gateway_performance: None, + }, + entry_point: EntryPoint::from(config.entry_gateway), + exit_point: ExitPoint::from(config.exit_router), + nym_ips: None, + nym_mtu: None, + dns: None, + disable_routing: false, + user_agent: Some(user_agent.clone()), + }; + + let task_manager = TaskManager::new(TASK_MANAGER_SHUTDOWN_TIMER_SECS).named("nym_vpn_lib"); + let gateway_directory_client = + GatewayClient::new(generic_config.gateway_config.clone(), user_agent.clone()).map_err( + |err| GatewayDirectoryError::FailedtoSetupGatewayDirectoryClient { + config: Box::new(generic_config.gateway_config.clone()), + source: err, + }, + )?; + + Ok(Self { + gateway_directory_client, + task_manager, + generic_config, + #[cfg(target_os = "ios")] + tun_provider: config.tun_provider, + shutdown_token, + }) + } + + pub async fn start(mut self) -> Result<()> { + let SelectedGateways { entry, exit } = self.select_gateways().await?; + let mixnet_client = self.start_mixnet_client(&entry).await?; + + let auth_addresses = match self.setup_auth_addresses(&entry, &exit) { + Ok(auth_addr) => auth_addr, + Err(err) => { + // Put in some manual error handling, the correct long-term solution is that handling + // errors and diconnecting the mixnet client needs to be unified down this code path + // and merged with the mix tunnel one. + mixnet_client.disconnect().await; + return Err(err); + } + }; + + let bandwidth_controller = + BandwidthController::new(mixnet_client.clone(), self.task_manager.subscribe()); + tokio::spawn(bandwidth_controller.run()); + + let mut shutdown_monitor_task_client = self.task_manager.subscribe(); + let cloned_shutdown_handle = self.shutdown_token.clone(); + + let mut set = JoinSet::new(); + set.spawn(async move { + let result = self.start_wireguard(mixnet_client, auth_addresses).await; + + // Shutdown task manager if the tunnel had to exit sooner. + if self.task_manager.signal_shutdown().is_err() { + tracing::error!("Failed to signal task manager shutdown."); + } + + // Wait until all tasks have been shut down + self.task_manager.wait_for_shutdown().await; + + result + }); + set.spawn(async move { + // Monitor for shutdown from task manager + shutdown_monitor_task_client.recv().await; + + // Cancel the tunnel if the task client exited sooner. + cloned_shutdown_handle.cancel(); + + Ok(()) + }); + + let mut result: Result<()> = Ok(()); + while let Some(res) = set.join_next().await { + match res { + Ok(Ok(_)) => {} + Ok(Err(e)) => { + result = Err(e); + } + Err(join_err) => { + tracing::error!("Failed to join task: {}", join_err); + } + } + } + + result + } + + async fn start_mixnet_client(&self, entry: &Gateway) -> Result { + tracing::info!("Setting up mixnet client"); + tracing::info!("Connecting to mixnet gateway: {}", entry.identity()); + + let mixnet_client = tokio::time::timeout( + MIXNET_CLIENT_STARTUP_TIMEOUT_SECS, + crate::mixnet::setup_mixnet_client( + entry.identity(), + &self.generic_config.data_path, + self.task_manager.subscribe_named("mixnet_client_main"), + self.generic_config.mixnet_client_config.clone(), + ), + ) + .await + .map_err(|_| Error::StartMixnetTimeout)? + .map_err(|e| Error::StartMixnetClient(Box::new(e)))?; + + Ok(mixnet_client) + } + + async fn select_gateways(&self) -> Result { + // The set of exit gateways is smaller than the set of entry gateways, so we start by selecting + // the exit gateway and then filter out the exit gateway from the set of entry gateways. + let all_gateways = self + .gateway_directory_client + .lookup_all_gateways() + .await + .map_err(|source| GatewayDirectoryError::FailedToLookupGateways { source })?; + let mut entry_gateways = all_gateways.clone(); + let exit_gateways = all_gateways; + + let exit_gateway = self + .generic_config + .exit_point + .lookup_gateway(&exit_gateways) + .map_err(|source| GatewayDirectoryError::FailedToSelectExitGateway { source })?; + + // Exclude the exit gateway from the list of entry gateways for privacy reasons + entry_gateways.remove_gateway(&exit_gateway); + + let entry_gateway = self + .generic_config + .entry_point + .lookup_gateway(&entry_gateways) + .await + .map_err(|source| match source { + nym_gateway_directory::Error::NoMatchingEntryGatewayForLocation { + requested_location, + available_countries: _, + } if Some(requested_location.as_str()) + == exit_gateway.two_letter_iso_country_code() => + { + GatewayDirectoryError::SameEntryAndExitGatewayFromCountry { + requested_location: requested_location.to_string(), + } + } + _ => GatewayDirectoryError::FailedToSelectEntryGateway { source }, + })?; + + tracing::info!("Found {} entry gateways", entry_gateways.len()); + tracing::info!("Found {} exit gateways", exit_gateways.len()); + tracing::info!( + "Using entry gateway: {}, location: {}, performance: {}", + *entry_gateway.identity(), + entry_gateway + .two_letter_iso_country_code() + .map_or_else(|| "unknown".to_string(), |code| code.to_string()), + entry_gateway + .performance + .map_or_else(|| "unknown".to_string(), |perf| perf.to_string()), + ); + tracing::info!( + "Using exit gateway: {}, location: {}, performance: {}", + *exit_gateway.identity(), + exit_gateway + .two_letter_iso_country_code() + .map_or_else(|| "unknown".to_string(), |code| code.to_string()), + entry_gateway + .performance + .map_or_else(|| "unknown".to_string(), |perf| perf.to_string()), + ); + tracing::info!( + "Using exit router address {}", + exit_gateway + .ipr_address + .map_or_else(|| "none".to_string(), |ipr| ipr.to_string()) + ); + + Ok(SelectedGateways { + entry: entry_gateway, + exit: exit_gateway, + }) + } + + fn setup_auth_addresses( + &self, + entry: &nym_gateway_directory::Gateway, + exit: &nym_gateway_directory::Gateway, + ) -> Result { + let entry_authenticator_address = entry + .authenticator_address + .ok_or(Error::AuthenticatorAddressNotFound)?; + let exit_authenticator_address = exit + .authenticator_address + .ok_or(Error::AuthenticatorAddressNotFound)?; + Ok(AuthAddresses::new( + entry_authenticator_address, + exit_authenticator_address, + )) + } + + async fn start_wireguard( + &self, + mixnet_client: SharedMixnetClient, + auth_addresses: AuthAddresses, + ) -> Result<()> { + let (Some(entry_auth_recipient), Some(exit_auth_recipient)) = + (auth_addresses.entry().0, auth_addresses.exit().0) + else { + return Err(Error::AuthenticationNotPossible(auth_addresses.to_string())); + }; + let auth_client = AuthClient::new_from_inner(mixnet_client.inner()).await; + + let wg_entry_config = self + .start_wg_entry_client(auth_client.clone(), entry_auth_recipient) + .await?; + let wg_exit_config = self + .start_wg_exit_client(auth_client.clone(), exit_auth_recipient) + .await?; + + tracing::info!("Created wg gateway clients"); + + TwoHopTunnel::start( + wg_entry_config, + wg_exit_config, + self.tun_provider.clone(), + self.shutdown_token.clone(), + ) + .await + .map_err(Error::Tunnel) + } + + async fn start_wg_entry_client( + &self, + auth_client: AuthClient, + recipient: Recipient, + ) -> Result { + let mut wg_entry_gateway_client = WgGatewayClient::new_entry( + &self.generic_config.data_path, + auth_client.clone(), + recipient, + ); + + let (gateway_data, _gateway_host) = + self.register_wg_key(&mut wg_entry_gateway_client).await?; + let key_pair = wg_entry_gateway_client.keypair(); + let node_config = WgNodeConfig::with_gateway_data(gateway_data, key_pair.private_key()); + + if wg_entry_gateway_client.suspended().await? { + return Err(Error::NotEnoughBandwidth); + } + + tokio::spawn( + wg_entry_gateway_client + .run(self.task_manager.subscribe_named("bandwidth_entry_client")), + ); + + Ok(node_config) + } + + async fn start_wg_exit_client( + &self, + auth_client: AuthClient, + recipient: Recipient, + ) -> Result { + let mut wg_exit_gateway_client = WgGatewayClient::new_exit( + &self.generic_config.data_path, + auth_client.clone(), + recipient, + ); + + let (gateway_data, _gateway_host) = + self.register_wg_key(&mut wg_exit_gateway_client).await?; + let key_pair = wg_exit_gateway_client.keypair(); + let node_config = WgNodeConfig::with_gateway_data(gateway_data, key_pair.private_key()); + + if wg_exit_gateway_client.suspended().await? { + return Err(Error::NotEnoughBandwidth); + } + + tokio::spawn( + wg_exit_gateway_client.run(self.task_manager.subscribe_named("bandwidth_exit_client")), + ); + + Ok(node_config) + } + + async fn register_wg_key( + &self, + wg_gateway_client: &mut WgGatewayClient, + ) -> Result<(GatewayData, IpAddr)> { + // First we need to register with the gateway to setup keys and IP assignment + tracing::info!("Registering with wireguard gateway"); + let gateway_id = wg_gateway_client + .auth_recipient() + .gateway() + .to_base58_string(); + let gateway_host = self + .gateway_directory_client + .lookup_gateway_ip(&gateway_id) + .await + .map_err(|source| GatewayDirectoryError::FailedToLookupGatewayIp { + gateway_id, + source, + })?; + let wg_gateway_data = wg_gateway_client.register_wireguard(gateway_host).await?; + tracing::debug!("Received wireguard gateway data: {wg_gateway_data:?}"); + Ok((wg_gateway_data, gateway_host)) + } +} + +struct SelectedGateways { + entry: nym_gateway_directory::Gateway, + exit: nym_gateway_directory::Gateway, +} + +impl WgNodeConfig { + fn with_gateway_data( + gateway_data: GatewayData, + private_key: &nym_crypto::asymmetric::encryption::PrivateKey, + ) -> Self { + Self { + interface: WgInterface { + listen_port: None, + private_key: PrivateKey::from(private_key.to_bytes()), + addresses: vec![IpNetwork::V4( + Ipv4Network::new(gateway_data.private_ipv4, 32) + .expect("private_ipv4/32 to ipnetwork"), + )], + dns: crate::DEFAULT_DNS_SERVERS.to_vec(), + mtu: 0, + }, + peer: WgPeer { + public_key: PublicKey::from(*gateway_data.public_key.as_bytes()), + endpoint: gateway_data.endpoint, + }, + } + } +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/tunnel_settings.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/tunnel_settings.rs new file mode 100644 index 0000000000..bb44703993 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/tunnel_settings.rs @@ -0,0 +1,265 @@ +//! Tunnel and network settings. + +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; +use itertools::{Either, Itertools}; + +#[derive(Debug)] +pub struct TunnelSettings { + /// Tunnel interface addresses. + pub interface_addresses: Vec, + + /// DNS servers to set on tunnel interface. + pub dns_servers: Vec, + + /// Tunnel remote addresses that will be excluded from being routed over the tunnel + /// to prevent the network loop. + pub remote_addresses: Vec, + + /// Tunnel device MTU. + pub mtu: u16, +} + +impl TunnelSettings { + /// Create tunnel network settings holding both networ and tunnel data necessary + /// for tunnel provider/vpn service configuration on mobile. + pub fn into_tunnel_network_settings(self) -> TunnelNetworkSettings { + let (interface_addrs_ipv4, interface_addrs_ipv6) = + Self::split_ipnet_addrs(self.interface_addresses); + let (bypass_addrs_ipv4, bypass_addrs_ipv6) = + Self::split_ipnet_addrs(Self::bypass_addresses(self.remote_addresses)); + + let ipv4_settings = if interface_addrs_ipv4.is_empty() { + None + } else { + Some(Self::ipv4_settings(interface_addrs_ipv4, bypass_addrs_ipv4)) + }; + + let ipv6_settings = if interface_addrs_ipv6.is_empty() { + None + } else { + Some(Self::ipv6_settings(interface_addrs_ipv6, bypass_addrs_ipv6)) + }; + + TunnelNetworkSettings { + tunnel_remote_address: "127.0.0.1".to_owned(), + ipv4_settings, + ipv6_settings, + dns_settings: Some(DnsSettings { + servers: self.dns_servers, + search_domains: None, + // Empty string tells packet tunnel to resolve all DNS queries using tunnel's DNS first. + // todo: this might be very ios specific knowledge. + match_domains: Some(vec!["".to_owned()]), + }), + mtu: self.mtu, + } + } + + fn ipv4_settings( + interface_addresses: Vec, + bypass_addresses: Vec, + ) -> Ipv4Settings { + let mut ipv4_settings = Ipv4Settings::new(interface_addresses.clone()); + + ipv4_settings.included_routes = Some( + interface_addresses + .into_iter() + .map(Ipv4Route::from) + .chain([Ipv4Route::Specific { + // todo: consider using Ipv4Route::Default + destination: Ipv4Addr::UNSPECIFIED, + subnet_mask: Ipv4Addr::UNSPECIFIED, + gateway: None, + }]) + .collect(), + ); + + if !bypass_addresses.is_empty() { + ipv4_settings.excluded_routes = + Some(bypass_addresses.into_iter().map(Ipv4Route::from).collect()) + } + + ipv4_settings + } + + fn ipv6_settings( + interface_addresses: Vec, + bypass_addresses: Vec, + ) -> Ipv6Settings { + let mut ipv6_settings = Ipv6Settings::new(interface_addresses.clone()); + + ipv6_settings.included_routes = Some( + interface_addresses + .into_iter() + .map(Ipv6Route::from) + .chain([Ipv6Route::Specific { + // todo: consider using Ipv6Route::Default + destination: Ipv6Addr::UNSPECIFIED, + prefix_length: 0, + gateway: None, + }]) + .collect(), + ); + + if !bypass_addresses.is_empty() { + ipv6_settings.excluded_routes = + Some(bypass_addresses.into_iter().map(Ipv6Route::from).collect()) + } + + ipv6_settings + } + + #[cfg(target_os = "ios")] + fn bypass_addresses(_remote_addresses: Vec) -> Vec { + // Do not bypass remote addresses since connections initiated within the packet tunnel + // bypass the tunnel interface anyway. + vec![] + } + + #[cfg(target_os = "android")] + fn bypass_addresses(remote_addresses: Vec) -> Vec { + remote_addresses + .into_iter() + .map(|ip_addr| match ip_addr { + IpAddr::V4(addr) => { + IpNetwork::V4(Ipv4Network::new(addr, 32).expect("remote_addr/32")) + } + IpAddr::V6(addr) => { + IpNetwork::V6(Ipv6Network::new(addr, 128).expect("remote_addr/128")) + } + }) + .collect() + } + + fn split_ipnet_addrs(ipnet_addrs: Vec) -> (Vec, Vec) { + ipnet_addrs.into_iter().partition_map(|addr| match addr { + IpNetwork::V4(address) => Either::Left( + Ipv4Network::new(address.ip(), address.prefix()) + .expect("failed to create ipv4 addr with /32 prefix"), + ), + IpNetwork::V6(address) => Either::Right( + Ipv6Network::new(address.ip(), address.prefix()) + .expect("failed to create ipv6 addr with /128 prefix"), + ), + }) + } +} + +#[derive(Debug, uniffi::Enum)] +pub enum Ipv4Route { + /// Default IPv4 route (0.0.0.0/0) + Default, + /// Individual IPv4 route + Specific { + destination: Ipv4Addr, + subnet_mask: Ipv4Addr, + gateway: Option, + }, +} + +impl From for Ipv4Route { + fn from(value: Ipv4Network) -> Self { + Ipv4Route::Specific { + destination: value.network(), + subnet_mask: value.mask(), + gateway: Some(value.ip()), + } + } +} + +#[derive(Debug, uniffi::Enum)] +pub enum Ipv6Route { + /// Default IPv6 route (::/0) + Default, + /// Individual IPv6 route + Specific { + destination: Ipv6Addr, + prefix_length: u8, + gateway: Option, + }, +} + +impl From for Ipv6Route { + fn from(value: Ipv6Network) -> Self { + Ipv6Route::Specific { + destination: value.network(), + prefix_length: value.prefix(), + gateway: Some(value.ip()), + } + } +} + +#[derive(Debug, Default, uniffi::Record)] +pub struct Ipv4Settings { + /// IPv4 addresses that will be set on tunnel interface. + pub addresses: Vec, + + /// Traffic matching these routes will be routed over the tun interface. + pub included_routes: Option>, + + /// Traffic matching these routes will be routed over the primary physical interface. + pub excluded_routes: Option>, +} + +impl Ipv4Settings { + pub fn new(addresses: Vec) -> Self { + Self { + addresses, + ..Default::default() + } + } +} + +#[derive(Debug, Default, uniffi::Record)] +pub struct Ipv6Settings { + /// IPv4 addresses that will be set on tunnel interface. + pub addresses: Vec, + + /// Traffic matching these routes will be routed over the tun interface. + pub included_routes: Option>, + + /// Traffic matching these routes will be routed over the primary physical interface. + pub excluded_routes: Option>, +} + +impl Ipv6Settings { + pub fn new(addresses: Vec) -> Self { + Self { + addresses, + ..Default::default() + } + } +} + +/// Tunnel + network settings +#[derive(Debug, uniffi::Record)] +pub struct TunnelNetworkSettings { + /// Tunnel remote address, which is mostly of decorative value. + pub tunnel_remote_address: String, + + /// IPv4 interface settings. + pub ipv4_settings: Option, + + /// IPv6 interface settings. + pub ipv6_settings: Option, + + /// DNS settings. + pub dns_settings: Option, + + /// Tunnel device MTU. + pub mtu: u16, +} + +#[derive(Debug, uniffi::Record)] +pub struct DnsSettings { + /// DNS IP addresses. + pub servers: Vec, + + /// DNS server search domains. + pub search_domains: Option>, + + /// Which domains to resolve using these DNS settings. + pub match_domains: Option>, +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/two_hop_config.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/two_hop_config.rs new file mode 100644 index 0000000000..3330e9c4a5 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/two_hop_config.rs @@ -0,0 +1,109 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + +use ipnetwork::IpNetwork; + +use super::wg_config::{WgInterface, WgNodeConfig, WgPeer}; + +/// Minimum IPv6 MTU that the hosts should be ready to accept. +const MIN_IPV6_MTU: u16 = 1280; + +/// WG tunnel overhead (IPv6) +const WG_TUNNEL_OVERHEAD: u16 = 80; + +/// Local port used for accepting exit traffic. +const UDP_FORWARDER_PORT: u16 = 34001; + +/// Local port used by exit tunnel when sending traffic to the udp forwarder. +const EXIT_WG_CLIENT_PORT: u16 = 54001; + +/// A struct that holds all configuration needed to setup the tunnels, tun device and forwarder. +#[derive(Debug)] +pub struct TwoHopConfig { + /// Entry configuration applied to netstack based WireGuard tunnel. + pub entry: WgNodeConfig, + + /// Exit configuration applied to wireguard-go attached to tun device. + pub exit: WgNodeConfig, + + /// Configuration for UDP forwader that's used for wrapping tunnel in tunnel. + pub forwarder: WgForwarderConfig, + + /// Tun device configuration. + pub tun: TunConfig, +} + +impl TwoHopConfig { + /// Create new two-hop configuration given two individual WireGuard configurations. + pub fn new(entry: WgNodeConfig, exit: WgNodeConfig) -> Self { + // Ensure that exit instance of wg attached on tun interface, uses a fixed port number + // to initiate connection to the udp forwarder, because it ignores traffic from other ports. + let client_port = exit.interface.listen_port.unwrap_or(EXIT_WG_CLIENT_PORT); + + let forwarder_config = WgForwarderConfig { + // Local endpoint that will forward exit traffic over entry tunnel + listen_endpoint: SocketAddr::new( + if exit.peer.endpoint.is_ipv4() { + IpAddr::V4(Ipv4Addr::LOCALHOST) + } else { + IpAddr::V6(Ipv6Addr::LOCALHOST) + }, + UDP_FORWARDER_PORT, + ), + exit_endpoint: exit.peer.endpoint, + client_port, + }; + + // Since we collect the exit traffic on tun, the tun's mtu must be lesser than entry mtu. + let exit_mtu = MIN_IPV6_MTU; + let entry_mtu = exit_mtu + WG_TUNNEL_OVERHEAD; + + let tun_config = TunConfig { + addresses: exit.interface.addresses.clone(), + dns: exit.interface.dns.clone(), + mtu: exit_mtu, + }; + + Self { + entry: WgNodeConfig { + interface: WgInterface { + mtu: entry_mtu, + ..entry.interface + }, + peer: entry.peer, + }, + exit: WgNodeConfig { + interface: WgInterface { + listen_port: Some(client_port), + mtu: exit_mtu, + ..exit.interface + }, + peer: WgPeer { + endpoint: forwarder_config.listen_endpoint, + ..exit.peer + }, + }, + forwarder: forwarder_config, + tun: tun_config, + } + } +} + +#[derive(Debug)] +pub struct WgForwarderConfig { + /// Local endpoint for collecting exit wg traffic. + pub listen_endpoint: SocketAddr, + + /// Actual exit endpoint. + pub exit_endpoint: SocketAddr, + + /// Client port from which the connection will be established to the listen endpoint. + /// Specified as listen_port in wg config. + pub client_port: u16, +} + +#[derive(Debug)] +pub struct TunConfig { + pub addresses: Vec, + pub dns: Vec, + pub mtu: u16, +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/two_hop_tunnel.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/two_hop_tunnel.rs new file mode 100644 index 0000000000..c3abd14c11 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/two_hop_tunnel.rs @@ -0,0 +1,221 @@ +use std::sync::Arc; + +#[cfg(target_os = "ios")] +use tokio::sync::mpsc; +use tokio_util::sync::CancellationToken; + +#[cfg(target_os = "ios")] +use super::ios::{ + default_path_observer::{DefaultPathObserver, DefaultPathReceiver, OSDefaultPath}, + dns64::Dns64Resolution, + tun, + tun_provider::OSTunProvider, +}; +use super::tunnel_settings::TunnelSettings; + +#[cfg(target_os = "ios")] +use super::wg_config::WgPeer; +use super::{two_hop_config::TwoHopConfig, wg_config::WgNodeConfig, Error, Result}; + +use nym_wg_go::{netstack, wireguard_go}; + +/// Two-hop WireGuard tunnel. +/// +/// ## Abstract +/// +/// In principle the two-hop WireGuard is implemented in the following way: +/// +/// * The tunnel to the entry node is established using wg/netstack. +/// * The UDP connection to the exit node is established over the entry tunnel. +/// * The exit traffic is captured on tun interface and directed towards local UDP forwarding proxy. +/// * The local UDP forwarding proxy injects all received UDP datagrams into the UDP connection to the exit node. +pub struct TwoHopTunnel { + /// Entry node tunnel + #[allow(unused)] + entry: netstack::Tunnel, + + /// Exit node tunnel + #[allow(unused)] + exit: wireguard_go::Tunnel, + + /// UDP connection over the entry tunnel, towards exit node. + #[allow(unused)] + exit_connection: Option, + + /// Cancellation token. + shutdown_token: CancellationToken, + + /// Entry peer configuration. + #[cfg(target_os = "ios")] + entry_peer: WgPeer, + + /// Interface for interacting with the iOS tunnel provider. + #[cfg(target_os = "ios")] + #[allow(unused)] + tun_provider: Arc, + + #[cfg(target_os = "ios")] + /// A conduit for receiving default path updates. + default_path_receiver: DefaultPathReceiver, +} + +impl TwoHopTunnel { + /// Start two-hop wg tunnel given entry and exit nodes. + pub async fn start( + entry_node_config: WgNodeConfig, + exit_node_config: WgNodeConfig, + #[cfg(target_os = "ios")] tun_provider: Arc, + shutdown_token: CancellationToken, + ) -> Result<()> { + // Save entry peer so that we can re-resolve it and update wg config on network changes. + #[cfg(target_os = "ios")] + let orig_entry_peer = entry_node_config.peer.clone(); + + let mut two_hop_config = TwoHopConfig::new(entry_node_config, exit_node_config); + + tracing::info!("Two-hop entry: {:#?}", two_hop_config.entry); + tracing::info!("Two-hop exit: {:#?}", two_hop_config.exit); + tracing::info!("Two-hop tun: {:#?}", two_hop_config.tun); + tracing::info!("Two-hop forwarder: {:#?}", two_hop_config.forwarder); + + // iOS does not perform dns64 resolution by default. Do that manually. + #[cfg(target_os = "ios")] + two_hop_config.entry.peer.resolve_in_place()?; + + // Obtain tunnel file descriptor and interface name. + #[cfg(target_os = "ios")] + let tun_fd = tun::get_tun_fd().ok_or(Error::CannotLocateTunFd)?; + #[cfg(target_os = "android")] + let tun_fd: std::os::fd::RawFd = todo!(); + tracing::debug!("Found tunnel fd: {}", tun_fd); + + #[cfg(target_os = "ios")] + let tun_name = tun::get_tun_ifname(tun_fd).ok_or(Error::ObtainTunName)?; + #[cfg(target_os = "android")] + let tun_name: String = todo!(); + tracing::debug!("Tunnel interface name: {}", tun_name); + + let tunnel_settings = TunnelSettings { + interface_addresses: two_hop_config.tun.addresses, + dns_servers: two_hop_config.tun.dns, + remote_addresses: vec![two_hop_config.entry.peer.endpoint.ip()], + mtu: two_hop_config.tun.mtu, + }; + + // Configure tun, dns and routing + #[cfg(target_os = "ios")] + { + tun_provider + .set_tunnel_network_settings(tunnel_settings.into_tunnel_network_settings()) + .await + .map_err(Error::SetNetworkSettings)?; + } + + // Transform wg config structs into what nym-wg-go expects. + let entry_wg_config = two_hop_config.entry.into_netstack_config(); + let exit_wg_config = two_hop_config.exit.into_wireguard_config(); + + // Create netstack wg connected to the entry node. + let mut entry_tunnel = netstack::Tunnel::start(entry_wg_config, |s| { + tracing::debug!(name = "wg-netstack", "{}", s); + })?; + + // Open connection to the exit node via entry node. + let exit_connection = entry_tunnel.open_connection( + two_hop_config.forwarder.listen_endpoint.port(), + two_hop_config.forwarder.client_port, + two_hop_config.forwarder.exit_endpoint, + )?; + + // Create exit tunnel capturing exit traffic on device and sending it to the local udp forwarder. + let exit_tunnel = wireguard_go::Tunnel::start(exit_wg_config, tun_fd, |s| { + tracing::debug!(name = "wg-go", "{}", s); + })?; + + // Setup default path observer. + #[cfg(target_os = "ios")] + let default_path_receiver = Self::add_default_path_observer(tun_provider.clone())?; + + let two_hop_tunnel = Self { + entry: entry_tunnel, + exit: exit_tunnel, + exit_connection: Some(exit_connection), + shutdown_token, + #[cfg(target_os = "ios")] + entry_peer: orig_entry_peer, + #[cfg(target_os = "ios")] + tun_provider, + #[cfg(target_os = "ios")] + default_path_receiver, + }; + + two_hop_tunnel.run().await; + + Ok(()) + } + + #[cfg(target_os = "android")] + async fn run(self) { + self.shutdown_token.cancelled().await; + tracing::debug!("Received shutdown."); + } + + #[cfg(target_os = "ios")] + async fn run(mut self) { + loop { + tokio::select! { + _ = self.shutdown_token.cancelled() => { + tracing::debug!("Received shutdown."); + break; + }, + Some(new_path) = self.default_path_receiver.recv() => { + self.on_network_path_change(new_path); + }, + else => { + tracing::debug!("Default path channel is closed. Proceeding to shutdown."); + break; + } + } + } + } + + #[cfg(target_os = "ios")] + fn add_default_path_observer( + tun_provider: Arc, + ) -> Result { + let (tx, rx) = mpsc::unbounded_channel(); + let observer = Arc::new(DefaultPathObserver::new(tx)); + + tun_provider + .set_default_path_observer(Some(observer.clone())) + .map_err(Error::SetDefaultPathObserver)?; + + Ok(rx) + } + + #[cfg(target_os = "ios")] + fn on_network_path_change(&mut self, new_path: OSDefaultPath) { + tracing::debug!("New default path: {:?}", new_path); + + // Depending on the network device is connected to, we may need to re-resolve the IP addresses. + // For instance when device connects to IPv4-only server from IPv6-only network, + // it needs to use an IPv4-mapped address, which can be received by re-resolving + // the original peer IP. + if let Err(e) = self.update_peers() { + tracing::error!("Failed to update peers on network change: {}", e); + } + + // Rebind wireguard-go on tun device. + self.exit.bump_sockets(); + } + + #[cfg(target_os = "ios")] + fn update_peers(&mut self) -> Result<()> { + let peer_update = self.entry_peer.resolved()?.into_peer_endpoint_update(); + + // Update wireguard-go configuration with re-resolved peer endpoints. + self.exit.update_peers(&[peer_update])?; + + Ok(()) + } +} diff --git a/nym-vpn-core/nym-vpn-lib/src/mobile/wg_config.rs b/nym-vpn-core/nym-vpn-lib/src/mobile/wg_config.rs new file mode 100644 index 0000000000..779d745a49 --- /dev/null +++ b/nym-vpn-core/nym-vpn-lib/src/mobile/wg_config.rs @@ -0,0 +1,104 @@ +use std::{ + fmt, + net::{IpAddr, SocketAddr}, +}; + +use ipnetwork::IpNetwork; +use nym_wg_go::{netstack, wireguard_go, PeerConfig, PeerEndpointUpdate, PrivateKey, PublicKey}; + +#[derive(Debug)] +pub struct WgNodeConfig { + /// Interface configuration + pub interface: WgInterface, + + /// Peer configuration + pub peer: WgPeer, +} + +pub struct WgInterface { + /// WG client port. + pub listen_port: Option, + + /// Private key used by wg client. + pub private_key: PrivateKey, + + /// Addresses assigned on wg interface. + pub addresses: Vec, + + /// DNS addresses. + pub dns: Vec, + + /// Device MTU. + pub mtu: u16, +} + +impl fmt::Debug for WgInterface { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("WgInterface") + .field("listen_port", &self.listen_port) + .field("private_key", &"(hidden)") + .field("address", &self.addresses) + .field("dns", &self.dns) + .field("mtu", &self.mtu) + .finish() + } +} + +#[derive(Debug, Clone)] +pub struct WgPeer { + /// Gateway public key. + pub public_key: PublicKey, + + /// Gateway endpoint + pub endpoint: SocketAddr, +} + +impl WgPeer { + pub fn into_peer_endpoint_update(self) -> PeerEndpointUpdate { + PeerEndpointUpdate { + public_key: self.public_key, + endpoint: self.endpoint, + } + } +} + +impl WgNodeConfig { + pub fn into_netstack_config(self) -> netstack::Config { + netstack::Config { + interface: netstack::InterfaceConfig { + private_key: self.interface.private_key, + local_addrs: self + .interface + .addresses + .into_iter() + .map(|x| x.ip()) + .collect(), + dns_addrs: self.interface.dns, + mtu: self.interface.mtu, + }, + peers: vec![PeerConfig { + // todo: limit to loopback? + allowed_ips: vec!["0.0.0.0/0".parse().unwrap(), "::/0".parse().unwrap()], + public_key: self.peer.public_key, + preshared_key: None, + endpoint: self.peer.endpoint, + }], + } + } + + pub fn into_wireguard_config(self) -> wireguard_go::Config { + wireguard_go::Config { + interface: wireguard_go::InterfaceConfig { + listen_port: self.interface.listen_port, + private_key: self.interface.private_key, + mtu: self.interface.mtu, + }, + peers: vec![PeerConfig { + public_key: self.peer.public_key, + preshared_key: None, + endpoint: self.peer.endpoint, + allowed_ips: vec!["0.0.0.0/0".parse().unwrap(), "::/0".parse().unwrap()], + }], + } + } +} diff --git a/nym-vpn-core/nym-vpn-lib/src/platform/mod.rs b/nym-vpn-core/nym-vpn-lib/src/platform/mod.rs index c0d0eb926e..e8ce4386c7 100644 --- a/nym-vpn-core/nym-vpn-lib/src/platform/mod.rs +++ b/nym-vpn-core/nym-vpn-lib/src/platform/mod.rs @@ -2,6 +2,32 @@ // SPDX-License-Identifier: Apache-2.0 #![cfg_attr(not(target_os = "macos"), allow(dead_code))] +#[cfg(target_os = "android")] +pub mod android; +pub(crate) mod error; +mod status_listener; +#[cfg(any(target_os = "ios", target_os = "macos"))] +pub mod swift; + +use lazy_static::lazy_static; +use log::*; + +use std::{ + path::PathBuf, + str::FromStr, + sync::atomic::{AtomicBool, Ordering}, + sync::Arc, + sync::Mutex as StdMutex, + time::SystemTime, +}; + +use talpid_core::mpsc::Sender; +use tokio::runtime::Runtime; +use tokio::sync::{Mutex, Notify}; +use tokio::task::JoinHandle; +use tokio_util::sync::CancellationToken; +use url::Url; + use self::error::FFIError; use crate::credentials::{check_credential_base58, import_credential_base58}; use crate::gateway_directory::GatewayClient; @@ -14,39 +40,34 @@ use crate::vpn::{ spawn_nym_vpn, MixnetVpn, NymVpn, NymVpnCtrlMessage, NymVpnExitError, NymVpnExitStatusMessage, NymVpnHandle, SpecificVpn, }; -use lazy_static::lazy_static; -use log::*; -use std::path::PathBuf; -use std::str::FromStr; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::time::SystemTime; -use talpid_core::mpsc::Sender; -use tokio::runtime::Runtime; -use tokio::sync::{Mutex, Notify}; -use url::Url; -#[cfg(target_os = "android")] -pub mod android; -pub(crate) mod error; -mod status_listener; -#[cfg(any(target_os = "ios", target_os = "macos"))] -pub mod swift; +#[cfg(target_os = "ios")] +use crate::mobile::{ios::tun_provider::OSTunProvider, runner::WgTunnelRunner}; lazy_static! { - static ref VPN_SHUTDOWN_HANDLE: Mutex>> = Mutex::new(None); + static ref VPN_SHUTDOWN_HANDLE: Mutex> = Mutex::new(None); static ref RUNNING: AtomicBool = AtomicBool::new(false); static ref RUNTIME: Runtime = Runtime::new().unwrap(); - static ref LISTENER: std::sync::Mutex>> = - std::sync::Mutex::new(None); + static ref LISTENER: StdMutex>> = StdMutex::new(None); } -async fn set_shutdown_handle(handle: Arc) -> Result<(), FFIError> { - let mut guard = VPN_SHUTDOWN_HANDLE.lock().await; +enum ShutdownHandle { + Notify(Arc), + + #[allow(unused)] + CancellationToken { + join_handle: JoinHandle<()>, + shutdown_token: CancellationToken, + }, +} + +async fn set_shutdown_handle(shutdown_handle: ShutdownHandle) -> Result<(), FFIError> { + let mut guard: tokio::sync::MutexGuard<'_, Option> = + VPN_SHUTDOWN_HANDLE.lock().await; if guard.is_some() { return Err(FFIError::VpnNotStopped); } - *guard = Some(handle); + *guard = Some(shutdown_handle); Ok(()) } @@ -65,19 +86,34 @@ pub(crate) fn uniffi_set_listener_status(status: StatusEvent) { } async fn stop_and_reset_shutdown_handle() -> Result<(), FFIError> { - debug!("Getting shutdown handle"); - let mut guard = VPN_SHUTDOWN_HANDLE.lock().await; - if let Some(sh) = &*guard { - debug!("notifying waiters"); - sh.notify_waiters(); - debug!("waiting for waiters to be notified"); - sh.notified().await; - debug!("waiters notified"); - } else { - return Err(FFIError::VpnNotStarted); + tracing::debug!("Getting shutdown handle"); + let shutdown_handle = VPN_SHUTDOWN_HANDLE + .lock() + .await + .take() + .ok_or(FFIError::VpnNotStarted)?; + + match shutdown_handle { + ShutdownHandle::Notify(sh) => { + tracing::debug!("Notifying waiters"); + sh.notify_waiters(); + tracing::debug!("Waiting for waiters to be notified"); + sh.notified().await; + tracing::debug!("Waiters notified"); + } + ShutdownHandle::CancellationToken { + join_handle, + shutdown_token, + } => { + tracing::debug!("Cancel shutdown token."); + shutdown_token.cancel(); + if let Err(e) = join_handle.await { + tracing::error!("Failed to join on shutdown handle task: {}", e); + } + } } - *guard = None; - debug!("VPN shutdown handle reset"); + + tracing::debug!("VPN shutdown handle reset"); uniffi_set_listener_status(StatusEvent::Tun(TunStatus::Down)); Ok(()) } @@ -93,7 +129,7 @@ async fn _async_run_vpn(vpn: SpecificVpn) -> Result<(Arc, NymVpnHandle), debug!("creating new stop handle"); let stop_handle = Arc::new(Notify::new()); debug!("new stop handle created"); - set_shutdown_handle(stop_handle.clone()).await?; + set_shutdown_handle(ShutdownHandle::Notify(stop_handle.clone())).await?; debug!("shutdown handle set with new stop handle"); let handle = spawn_nym_vpn(vpn)?; debug!("spawned vpn handle"); @@ -147,17 +183,15 @@ pub struct VPNConfig { pub exit_router: ExitPoint, pub enable_two_hop: bool, #[cfg(target_os = "ios")] - pub tun_provider: Arc, + pub tun_provider: Arc, pub credential_data_path: Option, pub tun_status_listener: Option>, } fn sync_run_vpn(config: VPNConfig) -> Result, FFIError> { - #[cfg(any(target_os = "ios", target_os = "macos"))] - crate::platform::swift::init_logs(); - #[cfg(target_os = "android")] let context = crate::platform::android::get_context().ok_or(FFIError::NoContext)?; + #[cfg(target_os = "android")] debug!("got android context to create new vpn"); let mut vpn = NymVpn::new_mixnet_vpn( @@ -179,11 +213,14 @@ fn sync_run_vpn(config: VPNConfig) -> Result, FFIError> { #[allow(non_snake_case)] #[uniffi::export] -pub fn runVPN(config: VPNConfig) -> Result<(), FFIError> { +pub fn startVPN(config: VPNConfig) -> Result<(), FFIError> { if RUNNING.fetch_or(true, Ordering::Relaxed) { return Err(FFIError::VpnAlreadyRunning); } + #[cfg(any(target_os = "ios", target_os = "macos"))] + crate::platform::swift::init_logs(); + LISTENER .lock() .unwrap() @@ -191,6 +228,47 @@ pub fn runVPN(config: VPNConfig) -> Result<(), FFIError> { uniffi_set_listener_status(StatusEvent::Tun(TunStatus::InitializingClient)); + #[cfg(target_os = "ios")] + if config.enable_two_hop { + RUNTIME.block_on(async move { + tracing::debug!("Starting VPN tunnel..."); + + let shutdown_token = CancellationToken::new(); + let cloned_shutdown_token = shutdown_token.clone(); + + let join_handle = tokio::spawn(async move { + // todo: set this only when two hop tunnel is actually up. + uniffi_set_listener_status(StatusEvent::Tun(TunStatus::Up)); + + match WgTunnelRunner::new(config, cloned_shutdown_token) { + Ok(tun_runner) => match tun_runner.start().await { + Ok(_) => { + tracing::debug!("Tunnel runner exited."); + } + Err(e) => { + tracing::error!("Tunnel runner exited with error: {}", e); + } + }, + Err(e) => { + tracing::error!("Failed to create the tunnel runner: {}", e); + } + } + + uniffi_set_listener_status(StatusEvent::Tun(TunStatus::Down)); + }); + + let shutdown_handle = ShutdownHandle::CancellationToken { + join_handle, + shutdown_token, + }; + if let Err(e) = set_shutdown_handle(shutdown_handle).await { + tracing::error!("Failed to set shutdown handle: {}", e); + } + }); + + return Ok(()); + } + debug!("Trying to run VPN"); let vpn = sync_run_vpn(config); debug!("Got VPN"); @@ -283,7 +361,10 @@ pub fn stopVPN() -> Result<(), FFIError> { } uniffi_set_listener_status(StatusEvent::Tun(TunStatus::Disconnecting)); debug!("Stopping VPN"); - RUNTIME.block_on(stop_vpn()) + + RUNTIME.block_on(stop_vpn())?; + + Ok(()) } async fn stop_vpn() -> Result<(), FFIError> { diff --git a/nym-vpn-core/nym-vpn-lib/src/platform/swift.rs b/nym-vpn-core/nym-vpn-lib/src/platform/swift.rs index 4a11156f1e..04dbee84ad 100644 --- a/nym-vpn-core/nym-vpn-lib/src/platform/swift.rs +++ b/nym-vpn-core/nym-vpn-lib/src/platform/swift.rs @@ -1,111 +1,29 @@ // Copyright 2023 - Nym Technologies SA // SPDX-License-Identifier: Apache-2.0 -use super::*; -use crate::routing::RoutingConfig; -use error::FFIError; -use ipnetwork::IpNetwork; +use log::LevelFilter; use oslog::OsLogger; -use std::fmt::Debug; -use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -use std::os::fd::RawFd; -use talpid_types::net::wireguard::{ - PeerConfig as WgPeerConfig, PresharedKey, PrivateKey, PublicKey, TunnelConfig as WgTunnelConfig, -}; pub fn init_logs() { - OsLogger::new("net.nymtech.vpn.agent") + let result = OsLogger::new("net.nymtech.vpn.agent") .level_filter(LevelFilter::Debug) - .category_level_filter("hyper", log::LevelFilter::Warn) - .category_level_filter("tokio_reactor", log::LevelFilter::Warn) - .category_level_filter("reqwest", log::LevelFilter::Warn) - .category_level_filter("mio", log::LevelFilter::Warn) - .category_level_filter("want", log::LevelFilter::Warn) - .category_level_filter("tungstenite", log::LevelFilter::Warn) - .category_level_filter("tokio_tungstenite", log::LevelFilter::Warn) - .category_level_filter("handlebars", log::LevelFilter::Warn) - .category_level_filter("sled", log::LevelFilter::Warn) - .init() - .expect("Could not init logs"); - debug!("Logger initialized"); -} - -#[derive(uniffi::Record, Clone)] -pub struct TunnelConfig { - pub private_key: PrivateKey, - pub addresses: Vec, -} - -impl From for TunnelConfig { - fn from(value: WgTunnelConfig) -> Self { - TunnelConfig { - private_key: value.private_key, - addresses: value.addresses, - } - } -} - -#[derive(uniffi::Record, Clone)] -pub struct PeerConfig { - pub public_key: PublicKey, - pub allowed_ips: Vec, - pub endpoint: SocketAddr, - pub psk: Option, -} - -impl From for PeerConfig { - fn from(value: WgPeerConfig) -> Self { - PeerConfig { - public_key: value.public_key, - allowed_ips: value.allowed_ips, - endpoint: value.endpoint, - psk: value.psk, - } - } -} - -#[derive(uniffi::Record, Clone)] -pub struct WgConfig { - pub tunnel: TunnelConfig, - pub peers: Vec, - pub ipv4_gateway: Ipv4Addr, - pub ipv6_gateway: Option, - pub mtu: u16, -} - -impl From for WgConfig { - fn from(value: talpid_wireguard::config::Config) -> Self { - WgConfig { - tunnel: value.tunnel.into(), - peers: value.peers.into_iter().map(Into::into).collect(), - ipv4_gateway: value.ipv4_gateway, - ipv6_gateway: value.ipv6_gateway, - mtu: value.mtu, + .category_level_filter("hyper", LevelFilter::Warn) + .category_level_filter("tokio_reactor", LevelFilter::Warn) + .category_level_filter("reqwest", LevelFilter::Warn) + .category_level_filter("mio", LevelFilter::Warn) + .category_level_filter("want", LevelFilter::Warn) + .category_level_filter("tungstenite", LevelFilter::Warn) + .category_level_filter("tokio_tungstenite", LevelFilter::Warn) + .category_level_filter("handlebars", LevelFilter::Warn) + .category_level_filter("sled", LevelFilter::Warn) + .init(); + + match result { + Ok(_) => { + tracing::debug!("Logger initialized"); } - } -} - -#[derive(uniffi::Record, Clone)] -pub struct NymConfig { - pub ipv4_addr: Ipv4Addr, - pub ipv6_addr: Ipv6Addr, - pub mtu: u16, - pub entry_mixnet_gateway_ip: Option, -} - -impl From for NymConfig { - fn from(value: RoutingConfig) -> Self { - NymConfig { - ipv4_addr: value.tun_ips().ipv4, - ipv6_addr: value.tun_ips().ipv6, - mtu: value.mtu(), - entry_mixnet_gateway_ip: None, + Err(e) => { + tracing::error!("Failed to initialize os_log: {}", e); } - } -} - -#[uniffi::export(with_foreign)] -pub trait OSTunProvider: Send + Sync + Debug { - fn configure_wg(&self, config: WgConfig) -> Result<(), FFIError>; - fn configure_nym(&self, config: NymConfig) -> Result; + }; } diff --git a/nym-vpn-core/nym-vpn-lib/src/routing.rs b/nym-vpn-core/nym-vpn-lib/src/routing.rs index 2191a969fa..faffa50922 100644 --- a/nym-vpn-core/nym-vpn-lib/src/routing.rs +++ b/nym-vpn-core/nym-vpn-lib/src/routing.rs @@ -2,19 +2,25 @@ // SPDX-License-Identifier: GPL-3.0-only use netdev::Interface; +#[cfg(not(target_os = "ios"))] +use std::collections::HashSet; use std::fmt::{Display, Formatter}; -use std::net::{Ipv4Addr, Ipv6Addr}; +use std::net::IpAddr; +#[cfg(windows)] +use std::net::Ipv4Addr; #[cfg(target_os = "android")] use std::os::fd::{AsRawFd, RawFd}; #[cfg(target_os = "android")] use std::sync::{Arc, Mutex}; -use std::{collections::HashSet, net::IpAddr}; use talpid_core::dns::DnsMonitor; use ipnetwork::IpNetwork; use netdev::interface::get_default_interface; use nym_ip_packet_requests::IpPair; -use talpid_routing::{Node, RequiredRoute, RouteManager}; +use talpid_routing::RouteManager; +#[cfg(not(target_os = "ios"))] +use talpid_routing::{Node, RequiredRoute}; + #[cfg(target_os = "android")] use talpid_tunnel::tun_provider::TunProvider; use tap::TapFallible; @@ -28,15 +34,6 @@ use crate::{ const DEFAULT_TUN_MTU: u16 = 1500; -fn default_dns_servers() -> Vec { - vec![ - IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)), - IpAddr::V4(Ipv4Addr::new(1, 0, 0, 1)), - IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1111)), - IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1001)), - ] -} - #[derive(Clone)] pub(crate) struct RoutingConfig { pub(crate) mixnet_tun_config: tun2::Configuration, @@ -103,12 +100,12 @@ impl RoutingConfig { } } - #[cfg(any(target_os = "ios", target_os = "macos", target_os = "android"))] + #[cfg(any(target_os = "ios", target_os = "android"))] pub(crate) fn tun_ips(&self) -> IpPair { self.tun_ips } - #[cfg(any(target_os = "ios", target_os = "macos"))] + #[cfg(target_os = "ios")] pub(crate) fn mtu(&self) -> u16 { self.mtu } @@ -137,6 +134,7 @@ impl std::fmt::Display for LanGatewayIp { } #[cfg_attr(not(target_os = "windows"), allow(unused_variables))] +#[cfg(not(target_os = "ios"))] fn get_tunnel_nodes(iface_name: &str) -> (Node, Node) { #[cfg(windows)] { @@ -178,7 +176,66 @@ pub(crate) fn replace_default_prefixes(network: IpNetwork) -> Vec { vec![network] } +#[cfg(target_os = "ios")] pub(crate) async fn setup_mixnet_routing( + _route_manager: &mut RouteManager, + config: RoutingConfig, + #[cfg(target_os = "ios")] ios_tun_provider: std::sync::Arc< + dyn crate::mobile::ios::tun_provider::OSTunProvider, + >, + _dns_monitor: &mut DnsMonitor, + dns: Option, +) -> Result { + let fd = + crate::mobile::ios::tun::get_tun_fd().ok_or(crate::mobile::Error::CannotLocateTunFd)?; + let mut tun_config = tun2::Configuration::default(); + tun_config.raw_fd(fd); + + let interface_addresses = config.tun_ips(); + let tunnel_settings = crate::mobile::tunnel_settings::TunnelSettings { + interface_addresses: vec![ + IpNetwork::new(IpAddr::V4(interface_addresses.ipv4), 32) + .expect("ipnetwork from v4/32 addr"), + IpNetwork::new(IpAddr::V6(interface_addresses.ipv6), 128) + .expect("ipnetwork from v6/128 addr"), + ], + dns_servers: dns + .map(|dns| vec![dns]) + .unwrap_or(crate::DEFAULT_DNS_SERVERS.to_vec()), + remote_addresses: vec![config.entry_mixnet_gateway_ip], + mtu: config.mtu(), + }; + + ios_tun_provider + .set_tunnel_network_settings(tunnel_settings.into_tunnel_network_settings()) + .await?; + + let dev = tun2::create_as_async(&tun_config) + .tap_err(|err| error!("Failed to attach to tun device: {}", err))?; + let device_name = dev.as_ref().tun_name().unwrap().to_string(); + info!( + "Attached to tun device {device_name} with ip={device_ip:?}", + device_name = device_name, + device_ip = dev + .as_ref() + .address() + .map(|ip| ip.to_string()) + .unwrap_or("None".to_string()) + ); + debug!("Attached to tun device {device_name}: ip={device_ip:?}, broadcast={device_broadcast:?}, netmask={device_netmask:?}, destination={device_destination:?}, mtu={device_mtu:?}", + device_name = device_name, + device_ip = dev.as_ref().address(), + device_broadcast = dev.as_ref().broadcast(), + device_netmask = dev.as_ref().netmask(), + device_destination = dev.as_ref().destination(), + device_mtu = dev.as_ref().mtu(), + ); + + Ok(dev) +} + +#[cfg(not(target_os = "ios"))] +pub async fn setup_mixnet_routing( route_manager: &mut RouteManager, config: RoutingConfig, #[cfg(target_os = "ios")] ios_tun_provider: std::sync::Arc< @@ -307,7 +364,9 @@ pub(crate) async fn setup_mixnet_routing( route_manager.add_routes(routes.collect()).await?; // Set the DNS server - let dns_servers = dns.map(|dns| vec![dns]).unwrap_or(default_dns_servers()); + let dns_servers = dns + .map(|dns| vec![dns]) + .unwrap_or(crate::DEFAULT_DNS_SERVERS.to_vec()); tokio::task::block_in_place(move || dns_monitor.set(&device_name, &dns_servers))?; Ok(dev) diff --git a/nym-vpn-core/nym-vpn-lib/src/uniffi_custom_impls.rs b/nym-vpn-core/nym-vpn-lib/src/uniffi_custom_impls.rs index 4a02228259..5d5c54a969 100644 --- a/nym-vpn-core/nym-vpn-lib/src/uniffi_custom_impls.rs +++ b/nym-vpn-core/nym-vpn-lib/src/uniffi_custom_impls.rs @@ -6,7 +6,7 @@ use crate::{ vpn::{MixnetConnectionInfo, MixnetExitConnectionInfo, NymVpnStatusMessage}, NodeIdentity, Recipient, UniffiCustomTypeConverter, }; -use ipnetwork::IpNetwork; +use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network}; use nym_bandwidth_controller_pre_ecash::BandwidthStatusMessage; use nym_connection_monitor::ConnectionMonitorStatus; use nym_gateway_directory::{EntryPoint as GwEntryPoint, ExitPoint as GwExitPoint}; @@ -24,6 +24,8 @@ uniffi::custom_type!(IpAddr, String); uniffi::custom_type!(PrivateKey, String); uniffi::custom_type!(PublicKey, String); uniffi::custom_type!(IpNetwork, String); +uniffi::custom_type!(Ipv4Network, String); +uniffi::custom_type!(Ipv6Network, String); uniffi::custom_type!(SocketAddr, String); uniffi::custom_type!(PresharedKey, String); uniffi::custom_type!(Url, String); @@ -156,6 +158,30 @@ impl UniffiCustomTypeConverter for IpNetwork { } } +impl UniffiCustomTypeConverter for Ipv4Network { + type Builtin = String; + + fn into_custom(val: Self::Builtin) -> uniffi::Result { + Ok(Ipv4Network::from_str(&val)?) + } + + fn from_custom(obj: Self) -> Self::Builtin { + obj.to_string() + } +} + +impl UniffiCustomTypeConverter for Ipv6Network { + type Builtin = String; + + fn into_custom(val: Self::Builtin) -> uniffi::Result { + Ok(Ipv6Network::from_str(&val)?) + } + + fn from_custom(obj: Self) -> Self::Builtin { + obj.to_string() + } +} + impl UniffiCustomTypeConverter for SocketAddr { type Builtin = String; diff --git a/nym-vpn-core/nym-vpn-lib/src/vpn/base.rs b/nym-vpn-core/nym-vpn-lib/src/vpn/base.rs index b20b9ecb95..e26ed41e0a 100644 --- a/nym-vpn-core/nym-vpn-lib/src/vpn/base.rs +++ b/nym-vpn-core/nym-vpn-lib/src/vpn/base.rs @@ -18,7 +18,7 @@ use tracing::{error, info}; use tun2::AsyncDevice; #[cfg(target_os = "ios")] -use crate::platform::swift::OSTunProvider; +use crate::mobile::ios::tun_provider::OSTunProvider; use crate::{ error::Result, tunnel_setup::{AllTunnelsSetup, TunnelSetup}, diff --git a/nym-vpn-core/nym-vpn-lib/src/vpn/mixnet.rs b/nym-vpn-core/nym-vpn-lib/src/vpn/mixnet.rs index 8701ef77d6..ddedc1619b 100644 --- a/nym-vpn-core/nym-vpn-lib/src/vpn/mixnet.rs +++ b/nym-vpn-core/nym-vpn-lib/src/vpn/mixnet.rs @@ -19,7 +19,7 @@ use talpid_routing::RouteManager; use talpid_tunnel::tun_provider::TunProvider; #[cfg(target_os = "ios")] -use crate::platform::swift::OSTunProvider; +use crate::mobile::ios::tun_provider::OSTunProvider; use crate::{error::Result, mixnet::SharedMixnetClient, routing, Error, GatewayDirectoryError}; use super::base::{GenericNymVpnConfig, NymVpn, ShadowHandle, Vpn}; diff --git a/nym-vpn-core/nym-vpn-lib/src/vpn/wireguard.rs b/nym-vpn-core/nym-vpn-lib/src/vpn/wireguard.rs index c629e1c962..481c2dff21 100644 --- a/nym-vpn-core/nym-vpn-lib/src/vpn/wireguard.rs +++ b/nym-vpn-core/nym-vpn-lib/src/vpn/wireguard.rs @@ -10,7 +10,7 @@ use nym_gateway_directory::{EntryPoint, ExitPoint, NodeIdentity}; use talpid_tunnel::tun_provider::TunProvider; #[cfg(target_os = "ios")] -use crate::platform::swift::OSTunProvider; +use crate::mobile::ios::tun_provider::OSTunProvider; use super::{ base::{GenericNymVpnConfig, ShadowHandle, Vpn}, diff --git a/nym-vpn-core/nym-vpn-lib/uniffi/nym_vpn_lib.swift b/nym-vpn-core/nym-vpn-lib/uniffi/nym_vpn_lib.swift index 12e7f391e6..4f82d0c5de 100644 --- a/nym-vpn-core/nym-vpn-lib/uniffi/nym_vpn_lib.swift +++ b/nym-vpn-core/nym-vpn-lib/uniffi/nym_vpn_lib.swift @@ -382,28 +382,28 @@ fileprivate class UniffiHandleMap { // Public interface members begin here. -fileprivate struct FfiConverterUInt16: FfiConverterPrimitive { - typealias FfiType = UInt16 - typealias SwiftType = UInt16 +fileprivate struct FfiConverterUInt8: FfiConverterPrimitive { + typealias FfiType = UInt8 + typealias SwiftType = UInt8 - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt16 { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt8 { return try lift(readInt(&buf)) } - public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + public static func write(_ value: UInt8, into buf: inout [UInt8]) { writeInt(&buf, lower(value)) } } -fileprivate struct FfiConverterInt32: FfiConverterPrimitive { - typealias FfiType = Int32 - typealias SwiftType = Int32 +fileprivate struct FfiConverterUInt16: FfiConverterPrimitive { + typealias FfiType = UInt16 + typealias SwiftType = UInt16 - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Int32 { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> UInt16 { return try lift(readInt(&buf)) } - public static func write(_ value: Int32, into buf: inout [UInt8]) { + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { writeInt(&buf, lower(value)) } } @@ -518,16 +518,20 @@ fileprivate struct FfiConverterTimestamp: FfiConverterRustBuffer { -public protocol OsTunProvider : AnyObject { - - func configureWg(config: WgConfig) throws +/** + * Types observing network changes. + */ +public protocol OsDefaultPathObserver : AnyObject { - func configureNym(config: NymConfig) throws -> Int32 + func onDefaultPathChange(newPath: OsDefaultPath) } -open class OsTunProviderImpl: - OsTunProvider { +/** + * Types observing network changes. + */ +open class OsDefaultPathObserverImpl: + OsDefaultPathObserver { fileprivate let pointer: UnsafeMutableRawPointer! /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. @@ -552,7 +556,7 @@ open class OsTunProviderImpl: } public func uniffiClonePointer() -> UnsafeMutableRawPointer { - return try! rustCall { uniffi_nym_vpn_lib_fn_clone_ostunprovider(self.pointer, $0) } + return try! rustCall { uniffi_nym_vpn_lib_fn_clone_osdefaultpathobserver(self.pointer, $0) } } // No primary constructor declared for this class. @@ -561,27 +565,19 @@ open class OsTunProviderImpl: return } - try! rustCall { uniffi_nym_vpn_lib_fn_free_ostunprovider(pointer, $0) } + try! rustCall { uniffi_nym_vpn_lib_fn_free_osdefaultpathobserver(pointer, $0) } } -open func configureWg(config: WgConfig)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_method_ostunprovider_configure_wg(self.uniffiClonePointer(), - FfiConverterTypeWgConfig.lower(config),$0 +open func onDefaultPathChange(newPath: OsDefaultPath) {try! rustCall() { + uniffi_nym_vpn_lib_fn_method_osdefaultpathobserver_on_default_path_change(self.uniffiClonePointer(), + FfiConverterTypeOSDefaultPath.lower(newPath),$0 ) } } -open func configureNym(config: NymConfig)throws -> Int32 { - return try FfiConverterInt32.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_method_ostunprovider_configure_nym(self.uniffiClonePointer(), - FfiConverterTypeNymConfig.lower(config),$0 - ) -}) -} - } // Magic number for the Rust proxy to call using the same mechanism as every other method, @@ -593,54 +589,251 @@ private let UNIFFI_CALLBACK_ERROR: Int32 = 1 private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2 // Put the implementation in a struct so we don't pollute the top-level namespace -fileprivate struct UniffiCallbackInterfaceOSTunProvider { +fileprivate struct UniffiCallbackInterfaceOSDefaultPathObserver { // Create the VTable using a series of closures. // Swift automatically converts these into C callback functions. - static var vtable: UniffiVTableCallbackInterfaceOsTunProvider = UniffiVTableCallbackInterfaceOsTunProvider( - configureWg: { ( + static var vtable: UniffiVTableCallbackInterfaceOsDefaultPathObserver = UniffiVTableCallbackInterfaceOsDefaultPathObserver( + onDefaultPathChange: { ( uniffiHandle: UInt64, - config: RustBuffer, + newPath: RustBuffer, uniffiOutReturn: UnsafeMutableRawPointer, uniffiCallStatus: UnsafeMutablePointer ) in let makeCall = { () throws -> () in - guard let uniffiObj = try? FfiConverterTypeOSTunProvider.handleMap.get(handle: uniffiHandle) else { + guard let uniffiObj = try? FfiConverterTypeOSDefaultPathObserver.handleMap.get(handle: uniffiHandle) else { throw UniffiInternalError.unexpectedStaleHandle } - return try uniffiObj.configureWg( - config: try FfiConverterTypeWgConfig.lift(config) + return uniffiObj.onDefaultPathChange( + newPath: try FfiConverterTypeOSDefaultPath.lift(newPath) ) } let writeReturn = { () } - uniffiTraitInterfaceCallWithError( + uniffiTraitInterfaceCall( callStatus: uniffiCallStatus, makeCall: makeCall, - writeReturn: writeReturn, + writeReturn: writeReturn + ) + }, + uniffiFree: { (uniffiHandle: UInt64) -> () in + let result = try? FfiConverterTypeOSDefaultPathObserver.handleMap.remove(handle: uniffiHandle) + if result == nil { + print("Uniffi callback interface OSDefaultPathObserver: handle missing in uniffiFree") + } + } + ) +} + +private func uniffiCallbackInitOSDefaultPathObserver() { + uniffi_nym_vpn_lib_fn_init_callback_vtable_osdefaultpathobserver(&UniffiCallbackInterfaceOSDefaultPathObserver.vtable) +} + +public struct FfiConverterTypeOSDefaultPathObserver: FfiConverter { + fileprivate static var handleMap = UniffiHandleMap() + + typealias FfiType = UnsafeMutableRawPointer + typealias SwiftType = OsDefaultPathObserver + + public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> OsDefaultPathObserver { + return OsDefaultPathObserverImpl(unsafeFromRawPointer: pointer) + } + + public static func lower(_ value: OsDefaultPathObserver) -> UnsafeMutableRawPointer { + guard let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: handleMap.insert(obj: value))) else { + fatalError("Cast to UnsafeMutableRawPointer failed") + } + return ptr + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsDefaultPathObserver { + let v: UInt64 = try readInt(&buf) + // The Rust code won't compile if a pointer won't fit in a UInt64. + // We have to go via `UInt` because that's the thing that's the size of a pointer. + let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v)) + if (ptr == nil) { + throw UniffiInternalError.unexpectedNullPointer + } + return try lift(ptr!) + } + + public static func write(_ value: OsDefaultPathObserver, into buf: inout [UInt8]) { + // This fiddling is because `Int` is the thing that's the same size as a pointer. + // The Rust code won't compile if a pointer won't fit in a `UInt64`. + writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value))))) + } +} + + + + +public func FfiConverterTypeOSDefaultPathObserver_lift(_ pointer: UnsafeMutableRawPointer) throws -> OsDefaultPathObserver { + return try FfiConverterTypeOSDefaultPathObserver.lift(pointer) +} + +public func FfiConverterTypeOSDefaultPathObserver_lower(_ value: OsDefaultPathObserver) -> UnsafeMutableRawPointer { + return FfiConverterTypeOSDefaultPathObserver.lower(value) +} + + + + +public protocol OsTunProvider : AnyObject { + + /** + * Set network settings including tun, dns, ip. + */ + func setTunnelNetworkSettings(tunnelSettings: TunnelNetworkSettings) async throws + + /** + * Set or unset the default path observer. + */ + func setDefaultPathObserver(observer: OsDefaultPathObserver?) throws + +} + +open class OsTunProviderImpl: + OsTunProvider { + fileprivate let pointer: UnsafeMutableRawPointer! + + /// Used to instantiate a [FFIObject] without an actual pointer, for fakes in tests, mostly. + public struct NoPointer { + public init() {} + } + + // TODO: We'd like this to be `private` but for Swifty reasons, + // we can't implement `FfiConverter` without making this `required` and we can't + // make it `required` without making it `public`. + required public init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) { + self.pointer = pointer + } + + /// This constructor can be used to instantiate a fake object. + /// - Parameter noPointer: Placeholder value so we can have a constructor separate from the default empty one that may be implemented for classes extending [FFIObject]. + /// + /// - Warning: + /// Any object instantiated with this constructor cannot be passed to an actual Rust-backed object. Since there isn't a backing [Pointer] the FFI lower functions will crash. + public init(noPointer: NoPointer) { + self.pointer = nil + } + + public func uniffiClonePointer() -> UnsafeMutableRawPointer { + return try! rustCall { uniffi_nym_vpn_lib_fn_clone_ostunprovider(self.pointer, $0) } + } + // No primary constructor declared for this class. + + deinit { + guard let pointer = pointer else { + return + } + + try! rustCall { uniffi_nym_vpn_lib_fn_free_ostunprovider(pointer, $0) } + } + + + + + /** + * Set network settings including tun, dns, ip. + */ +open func setTunnelNetworkSettings(tunnelSettings: TunnelNetworkSettings)async throws { + return + try await uniffiRustCallAsync( + rustFutureFunc: { + uniffi_nym_vpn_lib_fn_method_ostunprovider_set_tunnel_network_settings( + self.uniffiClonePointer(), + FfiConverterTypeTunnelNetworkSettings.lower(tunnelSettings) + ) + }, + pollFunc: ffi_nym_vpn_lib_rust_future_poll_void, + completeFunc: ffi_nym_vpn_lib_rust_future_complete_void, + freeFunc: ffi_nym_vpn_lib_rust_future_free_void, + liftFunc: { $0 }, + errorHandler: FfiConverterTypeFFIError.lift + ) +} + + /** + * Set or unset the default path observer. + */ +open func setDefaultPathObserver(observer: OsDefaultPathObserver?)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { + uniffi_nym_vpn_lib_fn_method_ostunprovider_set_default_path_observer(self.uniffiClonePointer(), + FfiConverterOptionTypeOSDefaultPathObserver.lower(observer),$0 + ) +} +} + + +} + + +// Put the implementation in a struct so we don't pollute the top-level namespace +fileprivate struct UniffiCallbackInterfaceOSTunProvider { + + // Create the VTable using a series of closures. + // Swift automatically converts these into C callback functions. + static var vtable: UniffiVTableCallbackInterfaceOsTunProvider = UniffiVTableCallbackInterfaceOsTunProvider( + setTunnelNetworkSettings: { ( + uniffiHandle: UInt64, + tunnelSettings: RustBuffer, + uniffiFutureCallback: @escaping UniffiForeignFutureCompleteVoid, + uniffiCallbackData: UInt64, + uniffiOutReturn: UnsafeMutablePointer + ) in + let makeCall = { + () async throws -> () in + guard let uniffiObj = try? FfiConverterTypeOSTunProvider.handleMap.get(handle: uniffiHandle) else { + throw UniffiInternalError.unexpectedStaleHandle + } + return try await uniffiObj.setTunnelNetworkSettings( + tunnelSettings: try FfiConverterTypeTunnelNetworkSettings.lift(tunnelSettings) + ) + } + + let uniffiHandleSuccess = { (returnValue: ()) in + uniffiFutureCallback( + uniffiCallbackData, + UniffiForeignFutureStructVoid( + callStatus: RustCallStatus() + ) + ) + } + let uniffiHandleError = { (statusCode, errorBuf) in + uniffiFutureCallback( + uniffiCallbackData, + UniffiForeignFutureStructVoid( + callStatus: RustCallStatus(code: statusCode, errorBuf: errorBuf) + ) + ) + } + let uniffiForeignFuture = uniffiTraitInterfaceCallAsyncWithError( + makeCall: makeCall, + handleSuccess: uniffiHandleSuccess, + handleError: uniffiHandleError, lowerError: FfiConverterTypeFFIError.lower ) + uniffiOutReturn.pointee = uniffiForeignFuture }, - configureNym: { ( + setDefaultPathObserver: { ( uniffiHandle: UInt64, - config: RustBuffer, - uniffiOutReturn: UnsafeMutablePointer, + observer: RustBuffer, + uniffiOutReturn: UnsafeMutableRawPointer, uniffiCallStatus: UnsafeMutablePointer ) in let makeCall = { - () throws -> Int32 in + () throws -> () in guard let uniffiObj = try? FfiConverterTypeOSTunProvider.handleMap.get(handle: uniffiHandle) else { throw UniffiInternalError.unexpectedStaleHandle } - return try uniffiObj.configureNym( - config: try FfiConverterTypeNymConfig.lift(config) + return try uniffiObj.setDefaultPathObserver( + observer: try FfiConverterOptionTypeOSDefaultPathObserver.lift(observer) ) } - let writeReturn = { uniffiOutReturn.pointee = FfiConverterInt32.lower($0) } + let writeReturn = { () } uniffiTraitInterfaceCallWithError( callStatus: uniffiCallStatus, makeCall: makeCall, @@ -990,377 +1183,623 @@ public func FfiConverterTypeTunnelStatusListener_lower(_ value: TunnelStatusList } -public struct Location { - public var twoLetterIsoCountryCode: String +public struct DnsSettings { + /** + * DNS IP addresses. + */ + public var servers: [IpAddr] + /** + * DNS server search domains. + */ + public var searchDomains: [String]? + /** + * Which domains to resolve using these DNS settings. + */ + public var matchDomains: [String]? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(twoLetterIsoCountryCode: String) { - self.twoLetterIsoCountryCode = twoLetterIsoCountryCode + public init( + /** + * DNS IP addresses. + */servers: [IpAddr], + /** + * DNS server search domains. + */searchDomains: [String]?, + /** + * Which domains to resolve using these DNS settings. + */matchDomains: [String]?) { + self.servers = servers + self.searchDomains = searchDomains + self.matchDomains = matchDomains } } -extension Location: Equatable, Hashable { - public static func ==(lhs: Location, rhs: Location) -> Bool { - if lhs.twoLetterIsoCountryCode != rhs.twoLetterIsoCountryCode { +extension DnsSettings: Equatable, Hashable { + public static func ==(lhs: DnsSettings, rhs: DnsSettings) -> Bool { + if lhs.servers != rhs.servers { + return false + } + if lhs.searchDomains != rhs.searchDomains { + return false + } + if lhs.matchDomains != rhs.matchDomains { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(twoLetterIsoCountryCode) + hasher.combine(servers) + hasher.combine(searchDomains) + hasher.combine(matchDomains) } } -public struct FfiConverterTypeLocation: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Location { +public struct FfiConverterTypeDnsSettings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> DnsSettings { return - try Location( - twoLetterIsoCountryCode: FfiConverterString.read(from: &buf) + try DnsSettings( + servers: FfiConverterSequenceTypeIpAddr.read(from: &buf), + searchDomains: FfiConverterOptionSequenceString.read(from: &buf), + matchDomains: FfiConverterOptionSequenceString.read(from: &buf) ) } - public static func write(_ value: Location, into buf: inout [UInt8]) { - FfiConverterString.write(value.twoLetterIsoCountryCode, into: &buf) + public static func write(_ value: DnsSettings, into buf: inout [UInt8]) { + FfiConverterSequenceTypeIpAddr.write(value.servers, into: &buf) + FfiConverterOptionSequenceString.write(value.searchDomains, into: &buf) + FfiConverterOptionSequenceString.write(value.matchDomains, into: &buf) } } -public func FfiConverterTypeLocation_lift(_ buf: RustBuffer) throws -> Location { - return try FfiConverterTypeLocation.lift(buf) +public func FfiConverterTypeDnsSettings_lift(_ buf: RustBuffer) throws -> DnsSettings { + return try FfiConverterTypeDnsSettings.lift(buf) } -public func FfiConverterTypeLocation_lower(_ value: Location) -> RustBuffer { - return FfiConverterTypeLocation.lower(value) +public func FfiConverterTypeDnsSettings_lower(_ value: DnsSettings) -> RustBuffer { + return FfiConverterTypeDnsSettings.lower(value) } -public struct MixConnectionInfo { - public var nymAddress: Recipient - public var entryGateway: NodeIdentity +public struct Ipv4Settings { + /** + * IPv4 addresses that will be set on tunnel interface. + */ + public var addresses: [Ipv4Network] + /** + * Traffic matching these routes will be routed over the tun interface. + */ + public var includedRoutes: [Ipv4Route]? + /** + * Traffic matching these routes will be routed over the primary physical interface. + */ + public var excludedRoutes: [Ipv4Route]? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(nymAddress: Recipient, entryGateway: NodeIdentity) { - self.nymAddress = nymAddress - self.entryGateway = entryGateway + public init( + /** + * IPv4 addresses that will be set on tunnel interface. + */addresses: [Ipv4Network], + /** + * Traffic matching these routes will be routed over the tun interface. + */includedRoutes: [Ipv4Route]?, + /** + * Traffic matching these routes will be routed over the primary physical interface. + */excludedRoutes: [Ipv4Route]?) { + self.addresses = addresses + self.includedRoutes = includedRoutes + self.excludedRoutes = excludedRoutes } } -extension MixConnectionInfo: Equatable, Hashable { - public static func ==(lhs: MixConnectionInfo, rhs: MixConnectionInfo) -> Bool { - if lhs.nymAddress != rhs.nymAddress { +extension Ipv4Settings: Equatable, Hashable { + public static func ==(lhs: Ipv4Settings, rhs: Ipv4Settings) -> Bool { + if lhs.addresses != rhs.addresses { return false } - if lhs.entryGateway != rhs.entryGateway { + if lhs.includedRoutes != rhs.includedRoutes { + return false + } + if lhs.excludedRoutes != rhs.excludedRoutes { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(nymAddress) - hasher.combine(entryGateway) + hasher.combine(addresses) + hasher.combine(includedRoutes) + hasher.combine(excludedRoutes) } } -public struct FfiConverterTypeMixConnectionInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MixConnectionInfo { +public struct FfiConverterTypeIpv4Settings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv4Settings { return - try MixConnectionInfo( - nymAddress: FfiConverterTypeRecipient.read(from: &buf), - entryGateway: FfiConverterTypeNodeIdentity.read(from: &buf) + try Ipv4Settings( + addresses: FfiConverterSequenceTypeIpv4Network.read(from: &buf), + includedRoutes: FfiConverterOptionSequenceTypeIpv4Route.read(from: &buf), + excludedRoutes: FfiConverterOptionSequenceTypeIpv4Route.read(from: &buf) ) } - public static func write(_ value: MixConnectionInfo, into buf: inout [UInt8]) { - FfiConverterTypeRecipient.write(value.nymAddress, into: &buf) - FfiConverterTypeNodeIdentity.write(value.entryGateway, into: &buf) + public static func write(_ value: Ipv4Settings, into buf: inout [UInt8]) { + FfiConverterSequenceTypeIpv4Network.write(value.addresses, into: &buf) + FfiConverterOptionSequenceTypeIpv4Route.write(value.includedRoutes, into: &buf) + FfiConverterOptionSequenceTypeIpv4Route.write(value.excludedRoutes, into: &buf) } } -public func FfiConverterTypeMixConnectionInfo_lift(_ buf: RustBuffer) throws -> MixConnectionInfo { - return try FfiConverterTypeMixConnectionInfo.lift(buf) +public func FfiConverterTypeIpv4Settings_lift(_ buf: RustBuffer) throws -> Ipv4Settings { + return try FfiConverterTypeIpv4Settings.lift(buf) } -public func FfiConverterTypeMixConnectionInfo_lower(_ value: MixConnectionInfo) -> RustBuffer { - return FfiConverterTypeMixConnectionInfo.lower(value) +public func FfiConverterTypeIpv4Settings_lower(_ value: Ipv4Settings) -> RustBuffer { + return FfiConverterTypeIpv4Settings.lower(value) } -public struct MixExitConnectionInfo { - public var exitGateway: NodeIdentity - public var exitIpr: Recipient - public var ips: IpPair +public struct Ipv6Settings { + /** + * IPv4 addresses that will be set on tunnel interface. + */ + public var addresses: [Ipv6Network] + /** + * Traffic matching these routes will be routed over the tun interface. + */ + public var includedRoutes: [Ipv6Route]? + /** + * Traffic matching these routes will be routed over the primary physical interface. + */ + public var excludedRoutes: [Ipv6Route]? // Default memberwise initializers are never public by default, so we // declare one manually. - public init(exitGateway: NodeIdentity, exitIpr: Recipient, ips: IpPair) { - self.exitGateway = exitGateway - self.exitIpr = exitIpr - self.ips = ips + public init( + /** + * IPv4 addresses that will be set on tunnel interface. + */addresses: [Ipv6Network], + /** + * Traffic matching these routes will be routed over the tun interface. + */includedRoutes: [Ipv6Route]?, + /** + * Traffic matching these routes will be routed over the primary physical interface. + */excludedRoutes: [Ipv6Route]?) { + self.addresses = addresses + self.includedRoutes = includedRoutes + self.excludedRoutes = excludedRoutes } } -extension MixExitConnectionInfo: Equatable, Hashable { - public static func ==(lhs: MixExitConnectionInfo, rhs: MixExitConnectionInfo) -> Bool { - if lhs.exitGateway != rhs.exitGateway { +extension Ipv6Settings: Equatable, Hashable { + public static func ==(lhs: Ipv6Settings, rhs: Ipv6Settings) -> Bool { + if lhs.addresses != rhs.addresses { return false } - if lhs.exitIpr != rhs.exitIpr { + if lhs.includedRoutes != rhs.includedRoutes { return false } - if lhs.ips != rhs.ips { + if lhs.excludedRoutes != rhs.excludedRoutes { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(exitGateway) - hasher.combine(exitIpr) - hasher.combine(ips) + hasher.combine(addresses) + hasher.combine(includedRoutes) + hasher.combine(excludedRoutes) } } -public struct FfiConverterTypeMixExitConnectionInfo: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MixExitConnectionInfo { +public struct FfiConverterTypeIpv6Settings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Settings { return - try MixExitConnectionInfo( - exitGateway: FfiConverterTypeNodeIdentity.read(from: &buf), - exitIpr: FfiConverterTypeRecipient.read(from: &buf), - ips: FfiConverterTypeIpPair.read(from: &buf) + try Ipv6Settings( + addresses: FfiConverterSequenceTypeIpv6Network.read(from: &buf), + includedRoutes: FfiConverterOptionSequenceTypeIpv6Route.read(from: &buf), + excludedRoutes: FfiConverterOptionSequenceTypeIpv6Route.read(from: &buf) ) } - public static func write(_ value: MixExitConnectionInfo, into buf: inout [UInt8]) { - FfiConverterTypeNodeIdentity.write(value.exitGateway, into: &buf) - FfiConverterTypeRecipient.write(value.exitIpr, into: &buf) - FfiConverterTypeIpPair.write(value.ips, into: &buf) + public static func write(_ value: Ipv6Settings, into buf: inout [UInt8]) { + FfiConverterSequenceTypeIpv6Network.write(value.addresses, into: &buf) + FfiConverterOptionSequenceTypeIpv6Route.write(value.includedRoutes, into: &buf) + FfiConverterOptionSequenceTypeIpv6Route.write(value.excludedRoutes, into: &buf) } } -public func FfiConverterTypeMixExitConnectionInfo_lift(_ buf: RustBuffer) throws -> MixExitConnectionInfo { - return try FfiConverterTypeMixExitConnectionInfo.lift(buf) +public func FfiConverterTypeIpv6Settings_lift(_ buf: RustBuffer) throws -> Ipv6Settings { + return try FfiConverterTypeIpv6Settings.lift(buf) } -public func FfiConverterTypeMixExitConnectionInfo_lower(_ value: MixExitConnectionInfo) -> RustBuffer { - return FfiConverterTypeMixExitConnectionInfo.lower(value) +public func FfiConverterTypeIpv6Settings_lower(_ value: Ipv6Settings) -> RustBuffer { + return FfiConverterTypeIpv6Settings.lower(value) } -public struct NymConfig { - public var ipv4Addr: Ipv4Addr - public var ipv6Addr: Ipv6Addr - public var mtu: UInt16 - public var entryMixnetGatewayIp: IpAddr? +public struct Location { + public var twoLetterIsoCountryCode: String // Default memberwise initializers are never public by default, so we // declare one manually. - public init(ipv4Addr: Ipv4Addr, ipv6Addr: Ipv6Addr, mtu: UInt16, entryMixnetGatewayIp: IpAddr?) { - self.ipv4Addr = ipv4Addr - self.ipv6Addr = ipv6Addr - self.mtu = mtu - self.entryMixnetGatewayIp = entryMixnetGatewayIp + public init(twoLetterIsoCountryCode: String) { + self.twoLetterIsoCountryCode = twoLetterIsoCountryCode } } -extension NymConfig: Equatable, Hashable { - public static func ==(lhs: NymConfig, rhs: NymConfig) -> Bool { - if lhs.ipv4Addr != rhs.ipv4Addr { - return false - } - if lhs.ipv6Addr != rhs.ipv6Addr { +extension Location: Equatable, Hashable { + public static func ==(lhs: Location, rhs: Location) -> Bool { + if lhs.twoLetterIsoCountryCode != rhs.twoLetterIsoCountryCode { return false } - if lhs.mtu != rhs.mtu { + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(twoLetterIsoCountryCode) + } +} + + +public struct FfiConverterTypeLocation: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Location { + return + try Location( + twoLetterIsoCountryCode: FfiConverterString.read(from: &buf) + ) + } + + public static func write(_ value: Location, into buf: inout [UInt8]) { + FfiConverterString.write(value.twoLetterIsoCountryCode, into: &buf) + } +} + + +public func FfiConverterTypeLocation_lift(_ buf: RustBuffer) throws -> Location { + return try FfiConverterTypeLocation.lift(buf) +} + +public func FfiConverterTypeLocation_lower(_ value: Location) -> RustBuffer { + return FfiConverterTypeLocation.lower(value) +} + + +public struct MixConnectionInfo { + public var nymAddress: Recipient + public var entryGateway: NodeIdentity + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init(nymAddress: Recipient, entryGateway: NodeIdentity) { + self.nymAddress = nymAddress + self.entryGateway = entryGateway + } +} + + + +extension MixConnectionInfo: Equatable, Hashable { + public static func ==(lhs: MixConnectionInfo, rhs: MixConnectionInfo) -> Bool { + if lhs.nymAddress != rhs.nymAddress { return false } - if lhs.entryMixnetGatewayIp != rhs.entryMixnetGatewayIp { + if lhs.entryGateway != rhs.entryGateway { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(ipv4Addr) - hasher.combine(ipv6Addr) - hasher.combine(mtu) - hasher.combine(entryMixnetGatewayIp) + hasher.combine(nymAddress) + hasher.combine(entryGateway) } } -public struct FfiConverterTypeNymConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NymConfig { +public struct FfiConverterTypeMixConnectionInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MixConnectionInfo { return - try NymConfig( - ipv4Addr: FfiConverterTypeIpv4Addr.read(from: &buf), - ipv6Addr: FfiConverterTypeIpv6Addr.read(from: &buf), - mtu: FfiConverterUInt16.read(from: &buf), - entryMixnetGatewayIp: FfiConverterOptionTypeIpAddr.read(from: &buf) + try MixConnectionInfo( + nymAddress: FfiConverterTypeRecipient.read(from: &buf), + entryGateway: FfiConverterTypeNodeIdentity.read(from: &buf) ) } - public static func write(_ value: NymConfig, into buf: inout [UInt8]) { - FfiConverterTypeIpv4Addr.write(value.ipv4Addr, into: &buf) - FfiConverterTypeIpv6Addr.write(value.ipv6Addr, into: &buf) - FfiConverterUInt16.write(value.mtu, into: &buf) - FfiConverterOptionTypeIpAddr.write(value.entryMixnetGatewayIp, into: &buf) + public static func write(_ value: MixConnectionInfo, into buf: inout [UInt8]) { + FfiConverterTypeRecipient.write(value.nymAddress, into: &buf) + FfiConverterTypeNodeIdentity.write(value.entryGateway, into: &buf) } } -public func FfiConverterTypeNymConfig_lift(_ buf: RustBuffer) throws -> NymConfig { - return try FfiConverterTypeNymConfig.lift(buf) +public func FfiConverterTypeMixConnectionInfo_lift(_ buf: RustBuffer) throws -> MixConnectionInfo { + return try FfiConverterTypeMixConnectionInfo.lift(buf) } -public func FfiConverterTypeNymConfig_lower(_ value: NymConfig) -> RustBuffer { - return FfiConverterTypeNymConfig.lower(value) +public func FfiConverterTypeMixConnectionInfo_lower(_ value: MixConnectionInfo) -> RustBuffer { + return FfiConverterTypeMixConnectionInfo.lower(value) } -public struct PeerConfig { - public var publicKey: PublicKey - public var allowedIps: [IpNetwork] - public var endpoint: SocketAddr - public var psk: PresharedKey? +public struct MixExitConnectionInfo { + public var exitGateway: NodeIdentity + public var exitIpr: Recipient + public var ips: IpPair // Default memberwise initializers are never public by default, so we // declare one manually. - public init(publicKey: PublicKey, allowedIps: [IpNetwork], endpoint: SocketAddr, psk: PresharedKey?) { - self.publicKey = publicKey - self.allowedIps = allowedIps - self.endpoint = endpoint - self.psk = psk + public init(exitGateway: NodeIdentity, exitIpr: Recipient, ips: IpPair) { + self.exitGateway = exitGateway + self.exitIpr = exitIpr + self.ips = ips } } -extension PeerConfig: Equatable, Hashable { - public static func ==(lhs: PeerConfig, rhs: PeerConfig) -> Bool { - if lhs.publicKey != rhs.publicKey { +extension MixExitConnectionInfo: Equatable, Hashable { + public static func ==(lhs: MixExitConnectionInfo, rhs: MixExitConnectionInfo) -> Bool { + if lhs.exitGateway != rhs.exitGateway { return false } - if lhs.allowedIps != rhs.allowedIps { + if lhs.exitIpr != rhs.exitIpr { + return false + } + if lhs.ips != rhs.ips { + return false + } + return true + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(exitGateway) + hasher.combine(exitIpr) + hasher.combine(ips) + } +} + + +public struct FfiConverterTypeMixExitConnectionInfo: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> MixExitConnectionInfo { + return + try MixExitConnectionInfo( + exitGateway: FfiConverterTypeNodeIdentity.read(from: &buf), + exitIpr: FfiConverterTypeRecipient.read(from: &buf), + ips: FfiConverterTypeIpPair.read(from: &buf) + ) + } + + public static func write(_ value: MixExitConnectionInfo, into buf: inout [UInt8]) { + FfiConverterTypeNodeIdentity.write(value.exitGateway, into: &buf) + FfiConverterTypeRecipient.write(value.exitIpr, into: &buf) + FfiConverterTypeIpPair.write(value.ips, into: &buf) + } +} + + +public func FfiConverterTypeMixExitConnectionInfo_lift(_ buf: RustBuffer) throws -> MixExitConnectionInfo { + return try FfiConverterTypeMixExitConnectionInfo.lift(buf) +} + +public func FfiConverterTypeMixExitConnectionInfo_lower(_ value: MixExitConnectionInfo) -> RustBuffer { + return FfiConverterTypeMixExitConnectionInfo.lower(value) +} + + +/** + * Represents a default network route used by the system. + */ +public struct OsDefaultPath { + /** + * Indicates whether the process is able to make connection through the given path. + */ + public var status: OsPathStatus + /** + * Set to true for interfaces that are considered expensive, such as when using cellular data plan. + */ + public var isExpensive: Bool + /** + * Set to true when using a constrained interface, such as when using low-data mode. + */ + public var isConstrained: Bool + + // Default memberwise initializers are never public by default, so we + // declare one manually. + public init( + /** + * Indicates whether the process is able to make connection through the given path. + */status: OsPathStatus, + /** + * Set to true for interfaces that are considered expensive, such as when using cellular data plan. + */isExpensive: Bool, + /** + * Set to true when using a constrained interface, such as when using low-data mode. + */isConstrained: Bool) { + self.status = status + self.isExpensive = isExpensive + self.isConstrained = isConstrained + } +} + + + +extension OsDefaultPath: Equatable, Hashable { + public static func ==(lhs: OsDefaultPath, rhs: OsDefaultPath) -> Bool { + if lhs.status != rhs.status { return false } - if lhs.endpoint != rhs.endpoint { + if lhs.isExpensive != rhs.isExpensive { return false } - if lhs.psk != rhs.psk { + if lhs.isConstrained != rhs.isConstrained { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(publicKey) - hasher.combine(allowedIps) - hasher.combine(endpoint) - hasher.combine(psk) + hasher.combine(status) + hasher.combine(isExpensive) + hasher.combine(isConstrained) } } -public struct FfiConverterTypePeerConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PeerConfig { +public struct FfiConverterTypeOSDefaultPath: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsDefaultPath { return - try PeerConfig( - publicKey: FfiConverterTypePublicKey.read(from: &buf), - allowedIps: FfiConverterSequenceTypeIpNetwork.read(from: &buf), - endpoint: FfiConverterTypeSocketAddr.read(from: &buf), - psk: FfiConverterOptionTypePresharedKey.read(from: &buf) + try OsDefaultPath( + status: FfiConverterTypeOSPathStatus.read(from: &buf), + isExpensive: FfiConverterBool.read(from: &buf), + isConstrained: FfiConverterBool.read(from: &buf) ) } - public static func write(_ value: PeerConfig, into buf: inout [UInt8]) { - FfiConverterTypePublicKey.write(value.publicKey, into: &buf) - FfiConverterSequenceTypeIpNetwork.write(value.allowedIps, into: &buf) - FfiConverterTypeSocketAddr.write(value.endpoint, into: &buf) - FfiConverterOptionTypePresharedKey.write(value.psk, into: &buf) + public static func write(_ value: OsDefaultPath, into buf: inout [UInt8]) { + FfiConverterTypeOSPathStatus.write(value.status, into: &buf) + FfiConverterBool.write(value.isExpensive, into: &buf) + FfiConverterBool.write(value.isConstrained, into: &buf) } } -public func FfiConverterTypePeerConfig_lift(_ buf: RustBuffer) throws -> PeerConfig { - return try FfiConverterTypePeerConfig.lift(buf) +public func FfiConverterTypeOSDefaultPath_lift(_ buf: RustBuffer) throws -> OsDefaultPath { + return try FfiConverterTypeOSDefaultPath.lift(buf) } -public func FfiConverterTypePeerConfig_lower(_ value: PeerConfig) -> RustBuffer { - return FfiConverterTypePeerConfig.lower(value) +public func FfiConverterTypeOSDefaultPath_lower(_ value: OsDefaultPath) -> RustBuffer { + return FfiConverterTypeOSDefaultPath.lower(value) } -public struct TunnelConfig { - public var privateKey: PrivateKey - public var addresses: [IpAddr] +/** + * Tunnel + network settings + */ +public struct TunnelNetworkSettings { + /** + * Tunnel remote address, which is mostly of decorative value. + */ + public var tunnelRemoteAddress: String + /** + * IPv4 interface settings. + */ + public var ipv4Settings: Ipv4Settings? + /** + * IPv6 interface settings. + */ + public var ipv6Settings: Ipv6Settings? + /** + * DNS settings. + */ + public var dnsSettings: DnsSettings? + /** + * Tunnel device MTU. + */ + public var mtu: UInt16 // Default memberwise initializers are never public by default, so we // declare one manually. - public init(privateKey: PrivateKey, addresses: [IpAddr]) { - self.privateKey = privateKey - self.addresses = addresses + public init( + /** + * Tunnel remote address, which is mostly of decorative value. + */tunnelRemoteAddress: String, + /** + * IPv4 interface settings. + */ipv4Settings: Ipv4Settings?, + /** + * IPv6 interface settings. + */ipv6Settings: Ipv6Settings?, + /** + * DNS settings. + */dnsSettings: DnsSettings?, + /** + * Tunnel device MTU. + */mtu: UInt16) { + self.tunnelRemoteAddress = tunnelRemoteAddress + self.ipv4Settings = ipv4Settings + self.ipv6Settings = ipv6Settings + self.dnsSettings = dnsSettings + self.mtu = mtu } } -extension TunnelConfig: Equatable, Hashable { - public static func ==(lhs: TunnelConfig, rhs: TunnelConfig) -> Bool { - if lhs.privateKey != rhs.privateKey { +extension TunnelNetworkSettings: Equatable, Hashable { + public static func ==(lhs: TunnelNetworkSettings, rhs: TunnelNetworkSettings) -> Bool { + if lhs.tunnelRemoteAddress != rhs.tunnelRemoteAddress { return false } - if lhs.addresses != rhs.addresses { + if lhs.ipv4Settings != rhs.ipv4Settings { + return false + } + if lhs.ipv6Settings != rhs.ipv6Settings { + return false + } + if lhs.dnsSettings != rhs.dnsSettings { + return false + } + if lhs.mtu != rhs.mtu { return false } return true } public func hash(into hasher: inout Hasher) { - hasher.combine(privateKey) - hasher.combine(addresses) + hasher.combine(tunnelRemoteAddress) + hasher.combine(ipv4Settings) + hasher.combine(ipv6Settings) + hasher.combine(dnsSettings) + hasher.combine(mtu) } } -public struct FfiConverterTypeTunnelConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunnelConfig { +public struct FfiConverterTypeTunnelNetworkSettings: FfiConverterRustBuffer { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunnelNetworkSettings { return - try TunnelConfig( - privateKey: FfiConverterTypePrivateKey.read(from: &buf), - addresses: FfiConverterSequenceTypeIpAddr.read(from: &buf) + try TunnelNetworkSettings( + tunnelRemoteAddress: FfiConverterString.read(from: &buf), + ipv4Settings: FfiConverterOptionTypeIpv4Settings.read(from: &buf), + ipv6Settings: FfiConverterOptionTypeIpv6Settings.read(from: &buf), + dnsSettings: FfiConverterOptionTypeDnsSettings.read(from: &buf), + mtu: FfiConverterUInt16.read(from: &buf) ) } - public static func write(_ value: TunnelConfig, into buf: inout [UInt8]) { - FfiConverterTypePrivateKey.write(value.privateKey, into: &buf) - FfiConverterSequenceTypeIpAddr.write(value.addresses, into: &buf) + public static func write(_ value: TunnelNetworkSettings, into buf: inout [UInt8]) { + FfiConverterString.write(value.tunnelRemoteAddress, into: &buf) + FfiConverterOptionTypeIpv4Settings.write(value.ipv4Settings, into: &buf) + FfiConverterOptionTypeIpv6Settings.write(value.ipv6Settings, into: &buf) + FfiConverterOptionTypeDnsSettings.write(value.dnsSettings, into: &buf) + FfiConverterUInt16.write(value.mtu, into: &buf) } } -public func FfiConverterTypeTunnelConfig_lift(_ buf: RustBuffer) throws -> TunnelConfig { - return try FfiConverterTypeTunnelConfig.lift(buf) +public func FfiConverterTypeTunnelNetworkSettings_lift(_ buf: RustBuffer) throws -> TunnelNetworkSettings { + return try FfiConverterTypeTunnelNetworkSettings.lift(buf) } -public func FfiConverterTypeTunnelConfig_lower(_ value: TunnelConfig) -> RustBuffer { - return FfiConverterTypeTunnelConfig.lower(value) +public func FfiConverterTypeTunnelNetworkSettings_lower(_ value: TunnelNetworkSettings) -> RustBuffer { + return FfiConverterTypeTunnelNetworkSettings.lower(value) } @@ -1500,87 +1939,6 @@ public func FfiConverterTypeVPNConfig_lower(_ value: VpnConfig) -> RustBuffer { } -public struct WgConfig { - public var tunnel: TunnelConfig - public var peers: [PeerConfig] - public var ipv4Gateway: Ipv4Addr - public var ipv6Gateway: Ipv6Addr? - public var mtu: UInt16 - - // Default memberwise initializers are never public by default, so we - // declare one manually. - public init(tunnel: TunnelConfig, peers: [PeerConfig], ipv4Gateway: Ipv4Addr, ipv6Gateway: Ipv6Addr?, mtu: UInt16) { - self.tunnel = tunnel - self.peers = peers - self.ipv4Gateway = ipv4Gateway - self.ipv6Gateway = ipv6Gateway - self.mtu = mtu - } -} - - - -extension WgConfig: Equatable, Hashable { - public static func ==(lhs: WgConfig, rhs: WgConfig) -> Bool { - if lhs.tunnel != rhs.tunnel { - return false - } - if lhs.peers != rhs.peers { - return false - } - if lhs.ipv4Gateway != rhs.ipv4Gateway { - return false - } - if lhs.ipv6Gateway != rhs.ipv6Gateway { - return false - } - if lhs.mtu != rhs.mtu { - return false - } - return true - } - - public func hash(into hasher: inout Hasher) { - hasher.combine(tunnel) - hasher.combine(peers) - hasher.combine(ipv4Gateway) - hasher.combine(ipv6Gateway) - hasher.combine(mtu) - } -} - - -public struct FfiConverterTypeWgConfig: FfiConverterRustBuffer { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> WgConfig { - return - try WgConfig( - tunnel: FfiConverterTypeTunnelConfig.read(from: &buf), - peers: FfiConverterSequenceTypePeerConfig.read(from: &buf), - ipv4Gateway: FfiConverterTypeIpv4Addr.read(from: &buf), - ipv6Gateway: FfiConverterOptionTypeIpv6Addr.read(from: &buf), - mtu: FfiConverterUInt16.read(from: &buf) - ) - } - - public static func write(_ value: WgConfig, into buf: inout [UInt8]) { - FfiConverterTypeTunnelConfig.write(value.tunnel, into: &buf) - FfiConverterSequenceTypePeerConfig.write(value.peers, into: &buf) - FfiConverterTypeIpv4Addr.write(value.ipv4Gateway, into: &buf) - FfiConverterOptionTypeIpv6Addr.write(value.ipv6Gateway, into: &buf) - FfiConverterUInt16.write(value.mtu, into: &buf) - } -} - - -public func FfiConverterTypeWgConfig_lift(_ buf: RustBuffer) throws -> WgConfig { - return try FfiConverterTypeWgConfig.lift(buf) -} - -public func FfiConverterTypeWgConfig_lower(_ value: WgConfig) -> RustBuffer { - return FfiConverterTypeWgConfig.lower(value) -} - - public struct WireguardConnectionInfo { public var gatewayId: NodeIdentity public var publicKey: String @@ -2116,123 +2474,352 @@ extension FfiError: Error { } // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum NymVpnStatus { +public enum Ipv4Route { - case mixConnectInfo(mixConnectionInfo: MixConnectionInfo, mixExitConnectionInfo: MixExitConnectionInfo - ) - case wgConnectInfo(entryConnectionInfo: WireguardConnectionInfo, exitConnectionInfo: WireguardConnectionInfo + /** + * Default IPv4 route (0.0.0.0/0) + */ + case `default` + /** + * Individual IPv4 route + */ + case specific(destination: Ipv4Addr, subnetMask: Ipv4Addr, gateway: Ipv4Addr? ) } -public struct FfiConverterTypeNymVpnStatus: FfiConverterRustBuffer { - typealias SwiftType = NymVpnStatus +public struct FfiConverterTypeIpv4Route: FfiConverterRustBuffer { + typealias SwiftType = Ipv4Route - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NymVpnStatus { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv4Route { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .mixConnectInfo(mixConnectionInfo: try FfiConverterTypeMixConnectionInfo.read(from: &buf), mixExitConnectionInfo: try FfiConverterTypeMixExitConnectionInfo.read(from: &buf) - ) + case 1: return .`default` - case 2: return .wgConnectInfo(entryConnectionInfo: try FfiConverterTypeWireguardConnectionInfo.read(from: &buf), exitConnectionInfo: try FfiConverterTypeWireguardConnectionInfo.read(from: &buf) + case 2: return .specific(destination: try FfiConverterTypeIpv4Addr.read(from: &buf), subnetMask: try FfiConverterTypeIpv4Addr.read(from: &buf), gateway: try FfiConverterOptionTypeIpv4Addr.read(from: &buf) ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: NymVpnStatus, into buf: inout [UInt8]) { + public static func write(_ value: Ipv4Route, into buf: inout [UInt8]) { switch value { - case let .mixConnectInfo(mixConnectionInfo,mixExitConnectionInfo): + case .`default`: writeInt(&buf, Int32(1)) - FfiConverterTypeMixConnectionInfo.write(mixConnectionInfo, into: &buf) - FfiConverterTypeMixExitConnectionInfo.write(mixExitConnectionInfo, into: &buf) - - case let .wgConnectInfo(entryConnectionInfo,exitConnectionInfo): + + case let .specific(destination,subnetMask,gateway): writeInt(&buf, Int32(2)) - FfiConverterTypeWireguardConnectionInfo.write(entryConnectionInfo, into: &buf) - FfiConverterTypeWireguardConnectionInfo.write(exitConnectionInfo, into: &buf) + FfiConverterTypeIpv4Addr.write(destination, into: &buf) + FfiConverterTypeIpv4Addr.write(subnetMask, into: &buf) + FfiConverterOptionTypeIpv4Addr.write(gateway, into: &buf) } } } -public func FfiConverterTypeNymVpnStatus_lift(_ buf: RustBuffer) throws -> NymVpnStatus { - return try FfiConverterTypeNymVpnStatus.lift(buf) +public func FfiConverterTypeIpv4Route_lift(_ buf: RustBuffer) throws -> Ipv4Route { + return try FfiConverterTypeIpv4Route.lift(buf) } -public func FfiConverterTypeNymVpnStatus_lower(_ value: NymVpnStatus) -> RustBuffer { - return FfiConverterTypeNymVpnStatus.lower(value) +public func FfiConverterTypeIpv4Route_lower(_ value: Ipv4Route) -> RustBuffer { + return FfiConverterTypeIpv4Route.lower(value) } -extension NymVpnStatus: Equatable, Hashable {} +extension Ipv4Route: Equatable, Hashable {} // Note that we don't yet support `indirect` for enums. // See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. -public enum TunStatus { +public enum Ipv6Route { - case up - case down - case initializingClient - case establishingConnection - case disconnecting + /** + * Default IPv6 route (::/0) + */ + case `default` + /** + * Individual IPv6 route + */ + case specific(destination: Ipv6Addr, prefixLength: UInt8, gateway: Ipv6Addr? + ) } -public struct FfiConverterTypeTunStatus: FfiConverterRustBuffer { - typealias SwiftType = TunStatus +public struct FfiConverterTypeIpv6Route: FfiConverterRustBuffer { + typealias SwiftType = Ipv6Route - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunStatus { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Route { let variant: Int32 = try readInt(&buf) switch variant { - case 1: return .up - - case 2: return .down - - case 3: return .initializingClient - - case 4: return .establishingConnection + case 1: return .`default` - case 5: return .disconnecting + case 2: return .specific(destination: try FfiConverterTypeIpv6Addr.read(from: &buf), prefixLength: try FfiConverterUInt8.read(from: &buf), gateway: try FfiConverterOptionTypeIpv6Addr.read(from: &buf) + ) default: throw UniffiInternalError.unexpectedEnumCase } } - public static func write(_ value: TunStatus, into buf: inout [UInt8]) { + public static func write(_ value: Ipv6Route, into buf: inout [UInt8]) { switch value { - case .up: + case .`default`: writeInt(&buf, Int32(1)) - case .down: + case let .specific(destination,prefixLength,gateway): writeInt(&buf, Int32(2)) - - - case .initializingClient: - writeInt(&buf, Int32(3)) - - - case .establishingConnection: - writeInt(&buf, Int32(4)) - - - case .disconnecting: - writeInt(&buf, Int32(5)) - + FfiConverterTypeIpv6Addr.write(destination, into: &buf) + FfiConverterUInt8.write(prefixLength, into: &buf) + FfiConverterOptionTypeIpv6Addr.write(gateway, into: &buf) + + } + } +} + + +public func FfiConverterTypeIpv6Route_lift(_ buf: RustBuffer) throws -> Ipv6Route { + return try FfiConverterTypeIpv6Route.lift(buf) +} + +public func FfiConverterTypeIpv6Route_lower(_ value: Ipv6Route) -> RustBuffer { + return FfiConverterTypeIpv6Route.lower(value) +} + + + +extension Ipv6Route: Equatable, Hashable {} + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum NymVpnStatus { + + case mixConnectInfo(mixConnectionInfo: MixConnectionInfo, mixExitConnectionInfo: MixExitConnectionInfo + ) + case wgConnectInfo(entryConnectionInfo: WireguardConnectionInfo, exitConnectionInfo: WireguardConnectionInfo + ) +} + + +public struct FfiConverterTypeNymVpnStatus: FfiConverterRustBuffer { + typealias SwiftType = NymVpnStatus + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NymVpnStatus { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .mixConnectInfo(mixConnectionInfo: try FfiConverterTypeMixConnectionInfo.read(from: &buf), mixExitConnectionInfo: try FfiConverterTypeMixExitConnectionInfo.read(from: &buf) + ) + + case 2: return .wgConnectInfo(entryConnectionInfo: try FfiConverterTypeWireguardConnectionInfo.read(from: &buf), exitConnectionInfo: try FfiConverterTypeWireguardConnectionInfo.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: NymVpnStatus, into buf: inout [UInt8]) { + switch value { + + + case let .mixConnectInfo(mixConnectionInfo,mixExitConnectionInfo): + writeInt(&buf, Int32(1)) + FfiConverterTypeMixConnectionInfo.write(mixConnectionInfo, into: &buf) + FfiConverterTypeMixExitConnectionInfo.write(mixExitConnectionInfo, into: &buf) + + + case let .wgConnectInfo(entryConnectionInfo,exitConnectionInfo): + writeInt(&buf, Int32(2)) + FfiConverterTypeWireguardConnectionInfo.write(entryConnectionInfo, into: &buf) + FfiConverterTypeWireguardConnectionInfo.write(exitConnectionInfo, into: &buf) + + } + } +} + + +public func FfiConverterTypeNymVpnStatus_lift(_ buf: RustBuffer) throws -> NymVpnStatus { + return try FfiConverterTypeNymVpnStatus.lift(buf) +} + +public func FfiConverterTypeNymVpnStatus_lower(_ value: NymVpnStatus) -> RustBuffer { + return FfiConverterTypeNymVpnStatus.lower(value) +} + + + +extension NymVpnStatus: Equatable, Hashable {} + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum OsPathStatus { + + /** + * The path cannot be evaluated. + */ + case invalid + /** + * The path is ready to be used for network connections. + */ + case satisfied + /** + * The path for network connections is not available, either due to lack of network + * connectivity or being prohibited by system policy. + */ + case unsatisfied + /** + * The path is not currently satisfied, but may become satisfied upon a connection attempt. + * This can be due to a service, such as a VPN or a cellular data connection not being activated. + */ + case satisfiable + /** + * Unknown path status was received. + * The raw variant code is contained in associated value. + */ + case unknown(Int64 + ) +} + + +public struct FfiConverterTypeOSPathStatus: FfiConverterRustBuffer { + typealias SwiftType = OsPathStatus + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> OsPathStatus { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .invalid + + case 2: return .satisfied + + case 3: return .unsatisfied + + case 4: return .satisfiable + + case 5: return .unknown(try FfiConverterInt64.read(from: &buf) + ) + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: OsPathStatus, into buf: inout [UInt8]) { + switch value { + + + case .invalid: + writeInt(&buf, Int32(1)) + + + case .satisfied: + writeInt(&buf, Int32(2)) + + + case .unsatisfied: + writeInt(&buf, Int32(3)) + + + case .satisfiable: + writeInt(&buf, Int32(4)) + + + case let .unknown(v1): + writeInt(&buf, Int32(5)) + FfiConverterInt64.write(v1, into: &buf) + + } + } +} + + +public func FfiConverterTypeOSPathStatus_lift(_ buf: RustBuffer) throws -> OsPathStatus { + return try FfiConverterTypeOSPathStatus.lift(buf) +} + +public func FfiConverterTypeOSPathStatus_lower(_ value: OsPathStatus) -> RustBuffer { + return FfiConverterTypeOSPathStatus.lower(value) +} + + + +extension OsPathStatus: Equatable, Hashable {} + + + +// Note that we don't yet support `indirect` for enums. +// See https://github.com/mozilla/uniffi-rs/issues/396 for further discussion. + +public enum TunStatus { + + case up + case down + case initializingClient + case establishingConnection + case disconnecting +} + + +public struct FfiConverterTypeTunStatus: FfiConverterRustBuffer { + typealias SwiftType = TunStatus + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> TunStatus { + let variant: Int32 = try readInt(&buf) + switch variant { + + case 1: return .up + + case 2: return .down + + case 3: return .initializingClient + + case 4: return .establishingConnection + + case 5: return .disconnecting + + default: throw UniffiInternalError.unexpectedEnumCase + } + } + + public static func write(_ value: TunStatus, into buf: inout [UInt8]) { + switch value { + + + case .up: + writeInt(&buf, Int32(1)) + + + case .down: + writeInt(&buf, Int32(2)) + + + case .initializingClient: + writeInt(&buf, Int32(3)) + + + case .establishingConnection: + writeInt(&buf, Int32(4)) + + + case .disconnecting: + writeInt(&buf, Int32(5)) + } } } @@ -2252,8 +2839,134 @@ extension TunStatus: Equatable, Hashable {} -fileprivate struct FfiConverterOptionTimestamp: FfiConverterRustBuffer { - typealias SwiftType = Date? +fileprivate struct FfiConverterOptionTimestamp: FfiConverterRustBuffer { + typealias SwiftType = Date? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTimestamp.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTimestamp.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeOSDefaultPathObserver: FfiConverterRustBuffer { + typealias SwiftType = OsDefaultPathObserver? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeOSDefaultPathObserver.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeOSDefaultPathObserver.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeTunnelStatusListener: FfiConverterRustBuffer { + typealias SwiftType = TunnelStatusListener? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeTunnelStatusListener.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeTunnelStatusListener.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeDnsSettings: FfiConverterRustBuffer { + typealias SwiftType = DnsSettings? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeDnsSettings.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeDnsSettings.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeIpv4Settings: FfiConverterRustBuffer { + typealias SwiftType = Ipv4Settings? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeIpv4Settings.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeIpv4Settings.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeIpv6Settings: FfiConverterRustBuffer { + typealias SwiftType = Ipv6Settings? + + public static func write(_ value: SwiftType, into buf: inout [UInt8]) { + guard let value = value else { + writeInt(&buf, Int8(0)) + return + } + writeInt(&buf, Int8(1)) + FfiConverterTypeIpv6Settings.write(value, into: &buf) + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { + switch try readInt(&buf) as Int8 { + case 0: return nil + case 1: return try FfiConverterTypeIpv6Settings.read(from: &buf) + default: throw UniffiInternalError.unexpectedOptionalTag + } + } +} + +fileprivate struct FfiConverterOptionTypeUserAgent: FfiConverterRustBuffer { + typealias SwiftType = UserAgent? public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { @@ -2261,20 +2974,20 @@ fileprivate struct FfiConverterOptionTimestamp: FfiConverterRustBuffer { return } writeInt(&buf, Int8(1)) - FfiConverterTimestamp.write(value, into: &buf) + FfiConverterTypeUserAgent.write(value, into: &buf) } public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTimestamp.read(from: &buf) + case 1: return try FfiConverterTypeUserAgent.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypeTunnelStatusListener: FfiConverterRustBuffer { - typealias SwiftType = TunnelStatusListener? +fileprivate struct FfiConverterOptionSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [String]? public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { @@ -2282,20 +2995,20 @@ fileprivate struct FfiConverterOptionTypeTunnelStatusListener: FfiConverterRustB return } writeInt(&buf, Int8(1)) - FfiConverterTypeTunnelStatusListener.write(value, into: &buf) + FfiConverterSequenceString.write(value, into: &buf) } public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypeTunnelStatusListener.read(from: &buf) + case 1: return try FfiConverterSequenceString.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypeUserAgent: FfiConverterRustBuffer { - typealias SwiftType = UserAgent? +fileprivate struct FfiConverterOptionSequenceTypeIpv4Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv4Route]? public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { @@ -2303,20 +3016,20 @@ fileprivate struct FfiConverterOptionTypeUserAgent: FfiConverterRustBuffer { return } writeInt(&buf, Int8(1)) - FfiConverterTypeUserAgent.write(value, into: &buf) + FfiConverterSequenceTypeIpv4Route.write(value, into: &buf) } public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypeUserAgent.read(from: &buf) + case 1: return try FfiConverterSequenceTypeIpv4Route.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypeIpAddr: FfiConverterRustBuffer { - typealias SwiftType = IpAddr? +fileprivate struct FfiConverterOptionSequenceTypeIpv6Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv6Route]? public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { @@ -2324,20 +3037,20 @@ fileprivate struct FfiConverterOptionTypeIpAddr: FfiConverterRustBuffer { return } writeInt(&buf, Int8(1)) - FfiConverterTypeIpAddr.write(value, into: &buf) + FfiConverterSequenceTypeIpv6Route.write(value, into: &buf) } public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypeIpAddr.read(from: &buf) + case 1: return try FfiConverterSequenceTypeIpv6Route.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypeIpv6Addr: FfiConverterRustBuffer { - typealias SwiftType = Ipv6Addr? +fileprivate struct FfiConverterOptionTypeIpv4Addr: FfiConverterRustBuffer { + typealias SwiftType = Ipv4Addr? public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { @@ -2345,20 +3058,20 @@ fileprivate struct FfiConverterOptionTypeIpv6Addr: FfiConverterRustBuffer { return } writeInt(&buf, Int8(1)) - FfiConverterTypeIpv6Addr.write(value, into: &buf) + FfiConverterTypeIpv4Addr.write(value, into: &buf) } public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypeIpv6Addr.read(from: &buf) + case 1: return try FfiConverterTypeIpv4Addr.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypePathBuf: FfiConverterRustBuffer { - typealias SwiftType = PathBuf? +fileprivate struct FfiConverterOptionTypeIpv6Addr: FfiConverterRustBuffer { + typealias SwiftType = Ipv6Addr? public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { @@ -2366,20 +3079,20 @@ fileprivate struct FfiConverterOptionTypePathBuf: FfiConverterRustBuffer { return } writeInt(&buf, Int8(1)) - FfiConverterTypePathBuf.write(value, into: &buf) + FfiConverterTypeIpv6Addr.write(value, into: &buf) } public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypePathBuf.read(from: &buf) + case 1: return try FfiConverterTypeIpv6Addr.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } } -fileprivate struct FfiConverterOptionTypePresharedKey: FfiConverterRustBuffer { - typealias SwiftType = PresharedKey? +fileprivate struct FfiConverterOptionTypePathBuf: FfiConverterRustBuffer { + typealias SwiftType = PathBuf? public static func write(_ value: SwiftType, into buf: inout [UInt8]) { guard let value = value else { @@ -2387,13 +3100,13 @@ fileprivate struct FfiConverterOptionTypePresharedKey: FfiConverterRustBuffer { return } writeInt(&buf, Int8(1)) - FfiConverterTypePresharedKey.write(value, into: &buf) + FfiConverterTypePathBuf.write(value, into: &buf) } public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType { switch try readInt(&buf) as Int8 { case 0: return nil - case 1: return try FfiConverterTypePresharedKey.read(from: &buf) + case 1: return try FfiConverterTypePathBuf.read(from: &buf) default: throw UniffiInternalError.unexpectedOptionalTag } } @@ -2420,6 +3133,28 @@ fileprivate struct FfiConverterOptionTypeUrl: FfiConverterRustBuffer { } } +fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer { + typealias SwiftType = [String] + + public static func write(_ value: [String], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterString.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [String] { + let len: Int32 = try readInt(&buf) + var seq = [String]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterString.read(from: &buf)) + } + return seq + } +} + fileprivate struct FfiConverterSequenceTypeLocation: FfiConverterRustBuffer { typealias SwiftType = [Location] @@ -2442,23 +3177,45 @@ fileprivate struct FfiConverterSequenceTypeLocation: FfiConverterRustBuffer { } } -fileprivate struct FfiConverterSequenceTypePeerConfig: FfiConverterRustBuffer { - typealias SwiftType = [PeerConfig] +fileprivate struct FfiConverterSequenceTypeIpv4Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv4Route] + + public static func write(_ value: [Ipv4Route], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeIpv4Route.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv4Route] { + let len: Int32 = try readInt(&buf) + var seq = [Ipv4Route]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeIpv4Route.read(from: &buf)) + } + return seq + } +} + +fileprivate struct FfiConverterSequenceTypeIpv6Route: FfiConverterRustBuffer { + typealias SwiftType = [Ipv6Route] - public static func write(_ value: [PeerConfig], into buf: inout [UInt8]) { + public static func write(_ value: [Ipv6Route], into buf: inout [UInt8]) { let len = Int32(value.count) writeInt(&buf, len) for item in value { - FfiConverterTypePeerConfig.write(item, into: &buf) + FfiConverterTypeIpv6Route.write(item, into: &buf) } } - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [PeerConfig] { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv6Route] { let len: Int32 = try readInt(&buf) - var seq = [PeerConfig]() + var seq = [Ipv6Route]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - seq.append(try FfiConverterTypePeerConfig.read(from: &buf)) + seq.append(try FfiConverterTypeIpv6Route.read(from: &buf)) } return seq } @@ -2486,23 +3243,45 @@ fileprivate struct FfiConverterSequenceTypeIpAddr: FfiConverterRustBuffer { } } -fileprivate struct FfiConverterSequenceTypeIpNetwork: FfiConverterRustBuffer { - typealias SwiftType = [IpNetwork] +fileprivate struct FfiConverterSequenceTypeIpv4Network: FfiConverterRustBuffer { + typealias SwiftType = [Ipv4Network] + + public static func write(_ value: [Ipv4Network], into buf: inout [UInt8]) { + let len = Int32(value.count) + writeInt(&buf, len) + for item in value { + FfiConverterTypeIpv4Network.write(item, into: &buf) + } + } + + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv4Network] { + let len: Int32 = try readInt(&buf) + var seq = [Ipv4Network]() + seq.reserveCapacity(Int(len)) + for _ in 0 ..< len { + seq.append(try FfiConverterTypeIpv4Network.read(from: &buf)) + } + return seq + } +} + +fileprivate struct FfiConverterSequenceTypeIpv6Network: FfiConverterRustBuffer { + typealias SwiftType = [Ipv6Network] - public static func write(_ value: [IpNetwork], into buf: inout [UInt8]) { + public static func write(_ value: [Ipv6Network], into buf: inout [UInt8]) { let len = Int32(value.count) writeInt(&buf, len) for item in value { - FfiConverterTypeIpNetwork.write(item, into: &buf) + FfiConverterTypeIpv6Network.write(item, into: &buf) } } - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [IpNetwork] { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> [Ipv6Network] { let len: Int32 = try readInt(&buf) - var seq = [IpNetwork]() + var seq = [Ipv6Network]() seq.reserveCapacity(Int(len)) for _ in 0 ..< len { - seq.append(try FfiConverterTypeIpNetwork.read(from: &buf)) + seq.append(try FfiConverterTypeIpv6Network.read(from: &buf)) } return seq } @@ -2543,40 +3322,6 @@ public func FfiConverterTypeIpAddr_lower(_ value: IpAddr) -> RustBuffer { -/** - * Typealias from the type name used in the UDL file to the builtin type. This - * is needed because the UDL type name is used in function/method signatures. - */ -public typealias IpNetwork = String -public struct FfiConverterTypeIpNetwork: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> IpNetwork { - return try FfiConverterString.read(from: &buf) - } - - public static func write(_ value: IpNetwork, into buf: inout [UInt8]) { - return FfiConverterString.write(value, into: &buf) - } - - public static func lift(_ value: RustBuffer) throws -> IpNetwork { - return try FfiConverterString.lift(value) - } - - public static func lower(_ value: IpNetwork) -> RustBuffer { - return FfiConverterString.lower(value) - } -} - - -public func FfiConverterTypeIpNetwork_lift(_ value: RustBuffer) throws -> IpNetwork { - return try FfiConverterTypeIpNetwork.lift(value) -} - -public func FfiConverterTypeIpNetwork_lower(_ value: IpNetwork) -> RustBuffer { - return FfiConverterTypeIpNetwork.lower(value) -} - - - /** * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. @@ -2649,66 +3394,32 @@ public func FfiConverterTypeIpv4Addr_lower(_ value: Ipv4Addr) -> RustBuffer { * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias Ipv6Addr = String -public struct FfiConverterTypeIpv6Addr: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Addr { - return try FfiConverterString.read(from: &buf) - } - - public static func write(_ value: Ipv6Addr, into buf: inout [UInt8]) { - return FfiConverterString.write(value, into: &buf) - } - - public static func lift(_ value: RustBuffer) throws -> Ipv6Addr { - return try FfiConverterString.lift(value) - } - - public static func lower(_ value: Ipv6Addr) -> RustBuffer { - return FfiConverterString.lower(value) - } -} - - -public func FfiConverterTypeIpv6Addr_lift(_ value: RustBuffer) throws -> Ipv6Addr { - return try FfiConverterTypeIpv6Addr.lift(value) -} - -public func FfiConverterTypeIpv6Addr_lower(_ value: Ipv6Addr) -> RustBuffer { - return FfiConverterTypeIpv6Addr.lower(value) -} - - - -/** - * Typealias from the type name used in the UDL file to the builtin type. This - * is needed because the UDL type name is used in function/method signatures. - */ -public typealias NodeIdentity = String -public struct FfiConverterTypeNodeIdentity: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NodeIdentity { +public typealias Ipv4Network = String +public struct FfiConverterTypeIpv4Network: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv4Network { return try FfiConverterString.read(from: &buf) } - public static func write(_ value: NodeIdentity, into buf: inout [UInt8]) { + public static func write(_ value: Ipv4Network, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - public static func lift(_ value: RustBuffer) throws -> NodeIdentity { + public static func lift(_ value: RustBuffer) throws -> Ipv4Network { return try FfiConverterString.lift(value) } - public static func lower(_ value: NodeIdentity) -> RustBuffer { + public static func lower(_ value: Ipv4Network) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypeNodeIdentity_lift(_ value: RustBuffer) throws -> NodeIdentity { - return try FfiConverterTypeNodeIdentity.lift(value) +public func FfiConverterTypeIpv4Network_lift(_ value: RustBuffer) throws -> Ipv4Network { + return try FfiConverterTypeIpv4Network.lift(value) } -public func FfiConverterTypeNodeIdentity_lower(_ value: NodeIdentity) -> RustBuffer { - return FfiConverterTypeNodeIdentity.lower(value) +public func FfiConverterTypeIpv4Network_lower(_ value: Ipv4Network) -> RustBuffer { + return FfiConverterTypeIpv4Network.lower(value) } @@ -2717,32 +3428,32 @@ public func FfiConverterTypeNodeIdentity_lower(_ value: NodeIdentity) -> RustBuf * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PathBuf = String -public struct FfiConverterTypePathBuf: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PathBuf { +public typealias Ipv6Addr = String +public struct FfiConverterTypeIpv6Addr: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Addr { return try FfiConverterString.read(from: &buf) } - public static func write(_ value: PathBuf, into buf: inout [UInt8]) { + public static func write(_ value: Ipv6Addr, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - public static func lift(_ value: RustBuffer) throws -> PathBuf { + public static func lift(_ value: RustBuffer) throws -> Ipv6Addr { return try FfiConverterString.lift(value) } - public static func lower(_ value: PathBuf) -> RustBuffer { + public static func lower(_ value: Ipv6Addr) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePathBuf_lift(_ value: RustBuffer) throws -> PathBuf { - return try FfiConverterTypePathBuf.lift(value) +public func FfiConverterTypeIpv6Addr_lift(_ value: RustBuffer) throws -> Ipv6Addr { + return try FfiConverterTypeIpv6Addr.lift(value) } -public func FfiConverterTypePathBuf_lower(_ value: PathBuf) -> RustBuffer { - return FfiConverterTypePathBuf.lower(value) +public func FfiConverterTypeIpv6Addr_lower(_ value: Ipv6Addr) -> RustBuffer { + return FfiConverterTypeIpv6Addr.lower(value) } @@ -2751,32 +3462,32 @@ public func FfiConverterTypePathBuf_lower(_ value: PathBuf) -> RustBuffer { * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PresharedKey = String -public struct FfiConverterTypePresharedKey: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PresharedKey { +public typealias Ipv6Network = String +public struct FfiConverterTypeIpv6Network: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> Ipv6Network { return try FfiConverterString.read(from: &buf) } - public static func write(_ value: PresharedKey, into buf: inout [UInt8]) { + public static func write(_ value: Ipv6Network, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - public static func lift(_ value: RustBuffer) throws -> PresharedKey { + public static func lift(_ value: RustBuffer) throws -> Ipv6Network { return try FfiConverterString.lift(value) } - public static func lower(_ value: PresharedKey) -> RustBuffer { + public static func lower(_ value: Ipv6Network) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePresharedKey_lift(_ value: RustBuffer) throws -> PresharedKey { - return try FfiConverterTypePresharedKey.lift(value) +public func FfiConverterTypeIpv6Network_lift(_ value: RustBuffer) throws -> Ipv6Network { + return try FfiConverterTypeIpv6Network.lift(value) } -public func FfiConverterTypePresharedKey_lower(_ value: PresharedKey) -> RustBuffer { - return FfiConverterTypePresharedKey.lower(value) +public func FfiConverterTypeIpv6Network_lower(_ value: Ipv6Network) -> RustBuffer { + return FfiConverterTypeIpv6Network.lower(value) } @@ -2785,32 +3496,32 @@ public func FfiConverterTypePresharedKey_lower(_ value: PresharedKey) -> RustBuf * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PrivateKey = String -public struct FfiConverterTypePrivateKey: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PrivateKey { +public typealias NodeIdentity = String +public struct FfiConverterTypeNodeIdentity: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> NodeIdentity { return try FfiConverterString.read(from: &buf) } - public static func write(_ value: PrivateKey, into buf: inout [UInt8]) { + public static func write(_ value: NodeIdentity, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - public static func lift(_ value: RustBuffer) throws -> PrivateKey { + public static func lift(_ value: RustBuffer) throws -> NodeIdentity { return try FfiConverterString.lift(value) } - public static func lower(_ value: PrivateKey) -> RustBuffer { + public static func lower(_ value: NodeIdentity) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePrivateKey_lift(_ value: RustBuffer) throws -> PrivateKey { - return try FfiConverterTypePrivateKey.lift(value) +public func FfiConverterTypeNodeIdentity_lift(_ value: RustBuffer) throws -> NodeIdentity { + return try FfiConverterTypeNodeIdentity.lift(value) } -public func FfiConverterTypePrivateKey_lower(_ value: PrivateKey) -> RustBuffer { - return FfiConverterTypePrivateKey.lower(value) +public func FfiConverterTypeNodeIdentity_lower(_ value: NodeIdentity) -> RustBuffer { + return FfiConverterTypeNodeIdentity.lower(value) } @@ -2819,32 +3530,32 @@ public func FfiConverterTypePrivateKey_lower(_ value: PrivateKey) -> RustBuffer * Typealias from the type name used in the UDL file to the builtin type. This * is needed because the UDL type name is used in function/method signatures. */ -public typealias PublicKey = String -public struct FfiConverterTypePublicKey: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PublicKey { +public typealias PathBuf = String +public struct FfiConverterTypePathBuf: FfiConverter { + public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> PathBuf { return try FfiConverterString.read(from: &buf) } - public static func write(_ value: PublicKey, into buf: inout [UInt8]) { + public static func write(_ value: PathBuf, into buf: inout [UInt8]) { return FfiConverterString.write(value, into: &buf) } - public static func lift(_ value: RustBuffer) throws -> PublicKey { + public static func lift(_ value: RustBuffer) throws -> PathBuf { return try FfiConverterString.lift(value) } - public static func lower(_ value: PublicKey) -> RustBuffer { + public static func lower(_ value: PathBuf) -> RustBuffer { return FfiConverterString.lower(value) } } -public func FfiConverterTypePublicKey_lift(_ value: RustBuffer) throws -> PublicKey { - return try FfiConverterTypePublicKey.lift(value) +public func FfiConverterTypePathBuf_lift(_ value: RustBuffer) throws -> PathBuf { + return try FfiConverterTypePathBuf.lift(value) } -public func FfiConverterTypePublicKey_lower(_ value: PublicKey) -> RustBuffer { - return FfiConverterTypePublicKey.lower(value) +public func FfiConverterTypePathBuf_lower(_ value: PathBuf) -> RustBuffer { + return FfiConverterTypePathBuf.lower(value) } @@ -2883,40 +3594,6 @@ public func FfiConverterTypeRecipient_lower(_ value: Recipient) -> RustBuffer { -/** - * Typealias from the type name used in the UDL file to the builtin type. This - * is needed because the UDL type name is used in function/method signatures. - */ -public typealias SocketAddr = String -public struct FfiConverterTypeSocketAddr: FfiConverter { - public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SocketAddr { - return try FfiConverterString.read(from: &buf) - } - - public static func write(_ value: SocketAddr, into buf: inout [UInt8]) { - return FfiConverterString.write(value, into: &buf) - } - - public static func lift(_ value: RustBuffer) throws -> SocketAddr { - return try FfiConverterString.lift(value) - } - - public static func lower(_ value: SocketAddr) -> RustBuffer { - return FfiConverterString.lower(value) - } -} - - -public func FfiConverterTypeSocketAddr_lift(_ value: RustBuffer) throws -> SocketAddr { - return try FfiConverterTypeSocketAddr.lift(value) -} - -public func FfiConverterTypeSocketAddr_lower(_ value: SocketAddr) -> RustBuffer { - return FfiConverterTypeSocketAddr.lower(value) -} - - - /** @@ -2958,6 +3635,118 @@ public func FfiConverterTypeUrl_lower(_ value: Url) -> RustBuffer { return FfiConverterTypeUrl.lower(value) } +private let UNIFFI_RUST_FUTURE_POLL_READY: Int8 = 0 +private let UNIFFI_RUST_FUTURE_POLL_MAYBE_READY: Int8 = 1 + +fileprivate let uniffiContinuationHandleMap = UniffiHandleMap>() + +fileprivate func uniffiRustCallAsync( + rustFutureFunc: () -> UInt64, + pollFunc: (UInt64, @escaping UniffiRustFutureContinuationCallback, UInt64) -> (), + completeFunc: (UInt64, UnsafeMutablePointer) -> F, + freeFunc: (UInt64) -> (), + liftFunc: (F) throws -> T, + errorHandler: ((RustBuffer) throws -> Error)? +) async throws -> T { + // Make sure to call uniffiEnsureInitialized() since future creation doesn't have a + // RustCallStatus param, so doesn't use makeRustCall() + uniffiEnsureInitialized() + let rustFuture = rustFutureFunc() + defer { + freeFunc(rustFuture) + } + var pollResult: Int8; + repeat { + pollResult = await withUnsafeContinuation { + pollFunc( + rustFuture, + uniffiFutureContinuationCallback, + uniffiContinuationHandleMap.insert(obj: $0) + ) + } + } while pollResult != UNIFFI_RUST_FUTURE_POLL_READY + + return try liftFunc(makeRustCall( + { completeFunc(rustFuture, $0) }, + errorHandler: errorHandler + )) +} + +// Callback handlers for an async calls. These are invoked by Rust when the future is ready. They +// lift the return value or error and resume the suspended function. +fileprivate func uniffiFutureContinuationCallback(handle: UInt64, pollResult: Int8) { + if let continuation = try? uniffiContinuationHandleMap.remove(handle: handle) { + continuation.resume(returning: pollResult) + } else { + print("uniffiFutureContinuationCallback invalid handle") + } +} +private func uniffiTraitInterfaceCallAsync( + makeCall: @escaping () async throws -> T, + handleSuccess: @escaping (T) -> (), + handleError: @escaping (Int8, RustBuffer) -> () +) -> UniffiForeignFuture { + let task = Task { + do { + handleSuccess(try await makeCall()) + } catch { + handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error))) + } + } + let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task) + return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree) + +} + +private func uniffiTraitInterfaceCallAsyncWithError( + makeCall: @escaping () async throws -> T, + handleSuccess: @escaping (T) -> (), + handleError: @escaping (Int8, RustBuffer) -> (), + lowerError: @escaping (E) -> RustBuffer +) -> UniffiForeignFuture { + let task = Task { + do { + handleSuccess(try await makeCall()) + } catch let error as E { + handleError(CALL_ERROR, lowerError(error)) + } catch { + handleError(CALL_UNEXPECTED_ERROR, FfiConverterString.lower(String(describing: error))) + } + } + let handle = UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.insert(obj: task) + return UniffiForeignFuture(handle: handle, free: uniffiForeignFutureFree) +} + +// Borrow the callback handle map implementation to store foreign future handles +// TODO: consolidate the handle-map code (https://github.com/mozilla/uniffi-rs/pull/1823) +fileprivate var UNIFFI_FOREIGN_FUTURE_HANDLE_MAP = UniffiHandleMap() + +// Protocol for tasks that handle foreign futures. +// +// Defining a protocol allows all tasks to be stored in the same handle map. This can't be done +// with the task object itself, since has generic parameters. +protocol UniffiForeignFutureTask { + func cancel() +} + +extension Task: UniffiForeignFutureTask {} + +private func uniffiForeignFutureFree(handle: UInt64) { + do { + let task = try UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.remove(handle: handle) + // Set the cancellation flag on the task. If it's still running, the code can check the + // cancellation flag or call `Task.checkCancellation()`. If the task has completed, this is + // a no-op. + task.cancel() + } catch { + print("uniffiForeignFutureFree: handle missing from handlemap") + } +} + +// For testing +public func uniffiForeignFutureHandleCountNymVpnLib() -> Int { + UNIFFI_FOREIGN_FUTURE_HANDLE_MAP.count +} public func checkCredential(credential: String)throws -> Date? { return try FfiConverterOptionTimestamp.lift(try rustCallWithError(FfiConverterTypeFFIError.lift) { uniffi_nym_vpn_lib_fn_func_checkcredential( @@ -3002,8 +3791,8 @@ public func importCredential(credential: String, path: String)throws -> Date? { ) }) } -public func runVpn(config: VpnConfig)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { - uniffi_nym_vpn_lib_fn_func_runvpn( +public func startVpn(config: VpnConfig)throws {try rustCallWithError(FfiConverterTypeFFIError.lift) { + uniffi_nym_vpn_lib_fn_func_startvpn( FfiConverterTypeVPNConfig.lower(config),$0 ) } @@ -3044,16 +3833,19 @@ private var initializationResult: InitializationResult { if (uniffi_nym_vpn_lib_checksum_func_importcredential() != 8591) { return InitializationResult.apiChecksumMismatch } - if (uniffi_nym_vpn_lib_checksum_func_runvpn() != 2496) { + if (uniffi_nym_vpn_lib_checksum_func_startvpn() != 17465) { return InitializationResult.apiChecksumMismatch } if (uniffi_nym_vpn_lib_checksum_func_stopvpn() != 23819) { return InitializationResult.apiChecksumMismatch } - if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_configure_wg() != 61728) { + if (uniffi_nym_vpn_lib_checksum_method_osdefaultpathobserver_on_default_path_change() != 43452) { + return InitializationResult.apiChecksumMismatch + } + if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_set_tunnel_network_settings() != 48304) { return InitializationResult.apiChecksumMismatch } - if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_configure_nym() != 42844) { + if (uniffi_nym_vpn_lib_checksum_method_ostunprovider_set_default_path_observer() != 18569) { return InitializationResult.apiChecksumMismatch } if (uniffi_nym_vpn_lib_checksum_method_tunnelstatuslistener_on_tun_status_change() != 55105) { @@ -3072,6 +3864,7 @@ private var initializationResult: InitializationResult { return InitializationResult.apiChecksumMismatch } + uniffiCallbackInitOSDefaultPathObserver() uniffiCallbackInitOSTunProvider() uniffiCallbackInitTunnelStatusListener() return InitializationResult.ok diff --git a/wireguard/libwg/libwg_netstack.go b/wireguard/libwg/libwg_netstack.go index e3d8eeca4d..fd698097ed 100644 --- a/wireguard/libwg/libwg_netstack.go +++ b/wireguard/libwg/libwg_netstack.go @@ -161,7 +161,7 @@ func wgNetOpenConnectionThroughTunnel(entryTunnelHandle int32, listenPort uint16 udpForwarder, err := udp_forwarder.New(forwarderConfig, dev.Net, dev.Logger) if err != nil { - dev.Errorf("Failed to parse create udp forwader: %v", err) + dev.Errorf("Failed to create udp forwarder: %v", err) return ERROR_GENERAL_FAILURE }