summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBug Magnet <marco.nikic@mullvad.net>2025-05-16 13:35:30 +0200
committerBug Magnet <marco.nikic@mullvad.net>2025-05-23 09:23:28 +0200
commit8e997549c3af645992e80f6356d71425b4a784d6 (patch)
tree854bc8db0b04d649922cfb3c559b362acb57d6d5
parenta9d8ce8abf4073f72fb94fb6a05fb6c1b43069c3 (diff)
downloadmullvadvpn-8e997549c3af645992e80f6356d71425b4a784d6.tar.xz
mullvadvpn-8e997549c3af645992e80f6356d71425b4a784d6.zip
Fix API breaking down when used without connectivity
-rw-r--r--ios/MullvadREST/ApiHandlers/AddressCache.swift2
-rw-r--r--ios/MullvadRESTTests/MullvadApiTests.swift4
-rw-r--r--ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift31
-rw-r--r--ios/MullvadRustRuntime/MullvadApiContext.swift15
-rw-r--r--ios/MullvadRustRuntime/include/mullvad_rust_runtime.h36
-rw-r--r--ios/MullvadTypes/AddressCacheProviding.swift25
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj8
-rw-r--r--ios/MullvadVPN/AppDelegate.swift3
-rw-r--r--ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift3
-rw-r--r--ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift3
-rw-r--r--mullvad-ios/src/api_client/access_method_resolver.rs13
-rw-r--r--mullvad-ios/src/api_client/address_cache_provider.rs68
-rw-r--r--mullvad-ios/src/api_client/mod.rs17
13 files changed, 213 insertions, 15 deletions
diff --git a/ios/MullvadREST/ApiHandlers/AddressCache.swift b/ios/MullvadREST/ApiHandlers/AddressCache.swift
index d098e91226..b75a515c01 100644
--- a/ios/MullvadREST/ApiHandlers/AddressCache.swift
+++ b/ios/MullvadREST/ApiHandlers/AddressCache.swift
@@ -11,7 +11,7 @@ import MullvadLogging
import MullvadTypes
extension REST {
- public final class AddressCache: @unchecked Sendable {
+ public final class AddressCache: AddressCacheProviding, @unchecked Sendable {
/// Logger.
private let logger = Logger(label: "AddressCache")
diff --git a/ios/MullvadRESTTests/MullvadApiTests.swift b/ios/MullvadRESTTests/MullvadApiTests.swift
index 506c7240f8..64c0eb371b 100644
--- a/ios/MullvadRESTTests/MullvadApiTests.swift
+++ b/ios/MullvadRESTTests/MullvadApiTests.swift
@@ -18,6 +18,7 @@ import XCTest
class MullvadApiTests: XCTestCase {
let encoder = JSONEncoder()
+ let addressCache = REST.AddressCache(canWriteToCache: false, fileCache: MemoryCache())
func makeApiProxy(port: UInt16) throws -> APIQuerying {
let shadowsocksLoader = ShadowsocksLoaderStub(configuration: ShadowsocksConfiguration(
@@ -38,7 +39,8 @@ class MullvadApiTests: XCTestCase {
accessMethodWrapper: initAccessMethodSettingsWrapper(
methods: accessMethodsRepository
.fetchAll()
- )
+ ),
+ addressCacheProvider: addressCache
)
let proxy = REST.MullvadAPIProxy(
diff --git a/ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift b/ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift
new file mode 100644
index 0000000000..4c731eaf3a
--- /dev/null
+++ b/ios/MullvadRustRuntime/MullvadAddressCacheProvider.swift
@@ -0,0 +1,31 @@
+//
+// MullvadAddressCacheProvider.swift
+// MullvadRustRuntime
+//
+// Created by Marco Nikic on 2025-05-15.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+import MullvadTypes
+
+public func iniSwiftAddressCacheWrapper(provider: DefaultAddressCacheProvider) -> SwiftAddressCacheWrapper {
+ let rawProvider = Unmanaged.passUnretained(provider).toOpaque()
+ return init_swift_address_cache_wrapper(rawProvider)
+}
+
+@_cdecl("swift_get_cached_endpoint")
+func getCacheEndpoint(rawAddressCacheProvider: UnsafeMutableRawPointer) -> UnsafePointer<CChar>! {
+ let addressCacheProvider = Unmanaged<DefaultAddressCacheProvider>.fromOpaque(rawAddressCacheProvider)
+ .takeUnretainedValue()
+ let cStr = addressCacheProvider.getCurrentEndpoint().description.toCStringPointer()
+ /**
+ `cStr` needs to shortly outlive the return of this function in order to get transformed into a `SocketAddr`
+ This is the simplest way to guarantee that the pointer returned does not get deallocated immediately
+ Or that no memory is leaked every time this function gets called
+ **/
+ DispatchQueue(label: "com.MullvadRustRuntime.DecallocateQueue").async {
+ cStr?.deallocate()
+ }
+ return cStr
+}
diff --git a/ios/MullvadRustRuntime/MullvadApiContext.swift b/ios/MullvadRustRuntime/MullvadApiContext.swift
index 5517348cc1..bd89ab891f 100644
--- a/ios/MullvadRustRuntime/MullvadApiContext.swift
+++ b/ios/MullvadRustRuntime/MullvadApiContext.swift
@@ -16,6 +16,8 @@ public struct MullvadApiContext: @unchecked Sendable {
public let context: SwiftApiContext
private let shadowsocksBridgeProvider: SwiftShadowsocksBridgeProviding!
private let shadowsocksBridgeProviderWrapper: SwiftShadowsocksLoaderWrapper!
+ private let addressCacheWrapper: SwiftAddressCacheWrapper!
+ private let addressCacheProvider: AddressCacheProviding!
public init(
host: String,
@@ -23,12 +25,17 @@ public struct MullvadApiContext: @unchecked Sendable {
domain: String,
disableTls: Bool = false,
shadowsocksProvider: SwiftShadowsocksBridgeProviding,
- accessMethodWrapper: SwiftAccessMethodSettingsWrapper
+ accessMethodWrapper: SwiftAccessMethodSettingsWrapper,
+ addressCacheProvider: AddressCacheProviding
) throws {
let bridgeProvider = SwiftShadowsocksBridgeProvider(provider: shadowsocksProvider)
self.shadowsocksBridgeProvider = bridgeProvider
self.shadowsocksBridgeProviderWrapper = initMullvadShadowsocksBridgeProvider(provider: bridgeProvider)
+ let defaultAddressCache = DefaultAddressCacheProvider(provider: addressCacheProvider)
+ self.addressCacheProvider = defaultAddressCache
+ self.addressCacheWrapper = iniSwiftAddressCacheWrapper(provider: defaultAddressCache)
+
context = switch disableTls {
case true:
mullvad_api_init_new_tls_disabled(
@@ -36,7 +43,8 @@ public struct MullvadApiContext: @unchecked Sendable {
address,
domain,
shadowsocksBridgeProviderWrapper,
- accessMethodWrapper
+ accessMethodWrapper,
+ addressCacheWrapper
)
case false:
mullvad_api_init_new(
@@ -44,7 +52,8 @@ public struct MullvadApiContext: @unchecked Sendable {
address,
domain,
shadowsocksBridgeProviderWrapper,
- accessMethodWrapper
+ accessMethodWrapper,
+ addressCacheWrapper
)
}
diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
index accd302d53..70c1428098 100644
--- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
+++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h
@@ -61,6 +61,14 @@ typedef struct SwiftShadowsocksLoaderWrapper {
struct SwiftShadowsocksLoaderWrapperContext _0;
} SwiftShadowsocksLoaderWrapper;
+typedef struct SwiftAddressCacheProviderContext {
+ const void *address_cache;
+} SwiftAddressCacheProviderContext;
+
+typedef struct SwiftAddressCacheWrapper {
+ struct SwiftAddressCacheProviderContext _0;
+} SwiftAddressCacheWrapper;
+
typedef struct SwiftCancelHandle {
struct RequestCancelHandle *ptr;
} SwiftCancelHandle;
@@ -161,7 +169,8 @@ struct SwiftApiContext mullvad_api_init_new_tls_disabled(const char *host,
const char *address,
const char *domain,
struct SwiftShadowsocksLoaderWrapper bridge_provider,
- struct SwiftAccessMethodSettingsWrapper settings_provider);
+ struct SwiftAccessMethodSettingsWrapper settings_provider,
+ struct SwiftAddressCacheWrapper address_cache);
/**
* # Safety
@@ -181,7 +190,8 @@ struct SwiftApiContext mullvad_api_init_new(const char *host,
const char *address,
const char *domain,
struct SwiftShadowsocksLoaderWrapper bridge_provider,
- struct SwiftAccessMethodSettingsWrapper settings_provider);
+ struct SwiftAccessMethodSettingsWrapper settings_provider,
+ struct SwiftAddressCacheWrapper address_cache);
/**
* # Safety
@@ -202,7 +212,8 @@ struct SwiftApiContext mullvad_api_init_inner(const char *host,
const char *domain,
bool disable_tls,
struct SwiftShadowsocksLoaderWrapper bridge_provider,
- struct SwiftAccessMethodSettingsWrapper settings_provider);
+ struct SwiftAccessMethodSettingsWrapper settings_provider,
+ struct SwiftAddressCacheWrapper address_cache);
/**
* Converts parameters into a `Box<AccessMethodSetting>` raw representation that
@@ -299,6 +310,25 @@ struct SwiftCancelHandle mullvad_ios_delete_account(struct SwiftApiContext api_c
const char *account_number);
/**
+ * Return the latest available endpoint, or a default one if none are cached
+ *
+ * # SAFETY
+ * `rawAddressCacheProvider` **must** be provided by a call to `init_swift_address_cache_wrapper`
+ * It is okay to persist it, and use it accross multiple threads.
+ */
+extern const char *swift_get_cached_endpoint(const void *rawAddressCacheProvider);
+
+/**
+ * Called by the Swift side in order to provide an object to rust that provides API addresses in a UTF-8 string form
+ *
+ * # SAFETY
+ * `address_cache` **must be** pointing to a valid instance of a `DefaultAddressCacheProvider`
+ * That instance's lifetime has to be equivalent to a `'static` lifetime in Rust
+ * This function does not take ownership of `address_cache`
+ */
+struct SwiftAddressCacheWrapper init_swift_address_cache_wrapper(const void *address_cache);
+
+/**
* # Safety
*
* `api_context` must be pointing to a valid instance of `SwiftApiContext`. A `SwiftApiContext` is created
diff --git a/ios/MullvadTypes/AddressCacheProviding.swift b/ios/MullvadTypes/AddressCacheProviding.swift
new file mode 100644
index 0000000000..059409bbd5
--- /dev/null
+++ b/ios/MullvadTypes/AddressCacheProviding.swift
@@ -0,0 +1,25 @@
+//
+// AddressCacheProviding.swift
+// MullvadTypes
+//
+// Created by Marco Nikic on 2025-05-15.
+// Copyright © 2025 Mullvad VPN AB. All rights reserved.
+//
+
+import Foundation
+
+public protocol AddressCacheProviding: Sendable {
+ func getCurrentEndpoint() -> AnyIPEndpoint
+}
+
+public final class DefaultAddressCacheProvider: AddressCacheProviding, Sendable {
+ let provider: AddressCacheProviding
+
+ public init(provider: AddressCacheProviding) {
+ self.provider = provider
+ }
+
+ public func getCurrentEndpoint() -> AnyIPEndpoint {
+ provider.getCurrentEndpoint()
+ }
+}
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index fb003e53b1..de1e60d6b1 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -788,6 +788,8 @@
A970C89D2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */; };
A9711B2B2D662AE3003DA71D /* SwiftConnectionModeProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9711B2A2D662AE3003DA71D /* SwiftConnectionModeProvider.swift */; };
A97275562CE36CAE00029F15 /* DaitaV2Parameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97275552CE36CAE00029F15 /* DaitaV2Parameters.swift */; };
+ A97687C82DD60D5D000D96E8 /* AddressCacheProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97687C72DD60D5D000D96E8 /* AddressCacheProviding.swift */; };
+ A97687CA2DD60F36000D96E8 /* MullvadAddressCacheProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97687C92DD60F36000D96E8 /* MullvadAddressCacheProvider.swift */; };
A97D25AE2B0BB18100946B2D /* ProtocolObfuscator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */; };
A97D25B02B0BB5C400946B2D /* ProtocolObfuscationStub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */; };
A97D25B22B0CB02D00946B2D /* ProtocolObfuscatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */; };
@@ -2343,6 +2345,8 @@
A970C89C2B29E38C000A7684 /* Socks5UsernamePasswordCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Socks5UsernamePasswordCommand.swift; sourceTree = "<group>"; };
A9711B2A2D662AE3003DA71D /* SwiftConnectionModeProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftConnectionModeProvider.swift; sourceTree = "<group>"; };
A97275552CE36CAE00029F15 /* DaitaV2Parameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DaitaV2Parameters.swift; sourceTree = "<group>"; };
+ A97687C72DD60D5D000D96E8 /* AddressCacheProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressCacheProviding.swift; sourceTree = "<group>"; };
+ A97687C92DD60F36000D96E8 /* MullvadAddressCacheProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MullvadAddressCacheProvider.swift; sourceTree = "<group>"; };
A97D25AD2B0BB18100946B2D /* ProtocolObfuscator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscator.swift; sourceTree = "<group>"; };
A97D25AF2B0BB5C400946B2D /* ProtocolObfuscationStub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscationStub.swift; sourceTree = "<group>"; };
A97D25B12B0CB02D00946B2D /* ProtocolObfuscatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtocolObfuscatorTests.swift; sourceTree = "<group>"; };
@@ -3063,6 +3067,7 @@
58E511E028DDB7F100B0BCDE /* WrappingError.swift */,
A9711B2A2D662AE3003DA71D /* SwiftConnectionModeProvider.swift */,
A98207EE2D9192A300654558 /* ShadowsocksBridgeProviding.swift */,
+ A97687C72DD60D5D000D96E8 /* AddressCacheProviding.swift */,
);
path = MullvadTypes;
sourceTree = "<group>";
@@ -4587,6 +4592,7 @@
A96D0B442D675F0400DD6C59 /* MullvadConnectionModeProvider.swift */,
A98207F02D91A0AC00654558 /* MullvadShadowsocksBridgeProvider.swift */,
A98207F22D9ACE4C00654558 /* MullvadAccessMethodReceiver.swift */,
+ A97687C92DD60F36000D96E8 /* MullvadAddressCacheProvider.swift */,
);
path = MullvadRustRuntime;
sourceTree = "<group>";
@@ -6755,6 +6761,7 @@
7A7AD14F2BF21EF200B30B3C /* NameInputFormatter.swift in Sources */,
58D22413294C90210029F5F8 /* RelayConstraints.swift in Sources */,
7AB401852DA53D5300522E17 /* NewAccountData.swift in Sources */,
+ A97687C82DD60D5D000D96E8 /* AddressCacheProviding.swift in Sources */,
A97275562CE36CAE00029F15 /* DaitaV2Parameters.swift in Sources */,
7AF9BE8C2A321D1F00DBFEDB /* RelayFilter.swift in Sources */,
58D22414294C90210029F5F8 /* RelayLocation.swift in Sources */,
@@ -6904,6 +6911,7 @@
7AB931242D43C2CA005FCEBA /* MullvadApiContext.swift in Sources */,
A9D9A4BB2C36D397004088DD /* EphemeralPeerNegotiator.swift in Sources */,
F0A89CB72D9D923300580C27 /* String+UnsafePointer.swift in Sources */,
+ A97687CA2DD60F36000D96E8 /* MullvadAddressCacheProvider.swift in Sources */,
A9D9A4B22C36D12D004088DD /* TunnelObfuscator.swift in Sources */,
A98207F12D91A0AC00654558 /* MullvadShadowsocksBridgeProvider.swift in Sources */,
7AB931262D43D22F005FCEBA /* MullvadApiResponse.swift in Sources */,
diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift
index 4664fa3b57..2590e2f1e7 100644
--- a/ios/MullvadVPN/AppDelegate.swift
+++ b/ios/MullvadVPN/AppDelegate.swift
@@ -109,7 +109,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
address: REST.defaultAPIEndpoint.description,
domain: REST.encryptedDNSHostname,
shadowsocksProvider: shadowsocksLoader,
- accessMethodWrapper: transportStrategy.opaqueAccessMethodSettingsWrapper
+ accessMethodWrapper: transportStrategy.opaqueAccessMethodSettingsWrapper,
+ addressCacheProvider: addressCache
)
accessMethodReceiver = MullvadAccessMethodReceiver(
diff --git a/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift
index 2169d0100d..cc496bba63 100644
--- a/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift
+++ b/ios/MullvadVPNTests/MullvadVPN/TunnelManager/TunnelManagerTests.swift
@@ -75,7 +75,8 @@ class TunnelManagerTests: XCTestCase {
address: REST.defaultAPIEndpoint.description,
domain: REST.encryptedDNSHostname,
shadowsocksProvider: shadowsocksLoader,
- accessMethodWrapper: transportStrategy.opaqueAccessMethodSettingsWrapper
+ accessMethodWrapper: transportStrategy.opaqueAccessMethodSettingsWrapper,
+ addressCacheProvider: addressCache
)
try SettingsManager.writeSettings(LatestTunnelSettings())
diff --git a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
index 3f2df2cbdb..8be5f883b9 100644
--- a/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
+++ b/ios/PacketTunnel/PacketTunnelProvider/PacketTunnelProvider.swift
@@ -266,7 +266,8 @@ class PacketTunnelProvider: NEPacketTunnelProvider, @unchecked Sendable {
address: REST.defaultAPIEndpoint.description,
domain: REST.encryptedDNSHostname,
shadowsocksProvider: shadowsocksLoader,
- accessMethodWrapper: transportStrategy.opaqueAccessMethodSettingsWrapper
+ accessMethodWrapper: transportStrategy.opaqueAccessMethodSettingsWrapper,
+ addressCacheProvider: addressCache
)
accessMethodReceiver = MullvadAccessMethodReceiver(
diff --git a/mullvad-ios/src/api_client/access_method_resolver.rs b/mullvad-ios/src/api_client/access_method_resolver.rs
index 5ce22cc5cc..df8910fcbb 100644
--- a/mullvad-ios/src/api_client/access_method_resolver.rs
+++ b/mullvad-ios/src/api_client/access_method_resolver.rs
@@ -10,7 +10,10 @@ use talpid_types::net::{
};
use tonic::async_trait;
-use super::shadowsocks_loader::SwiftShadowsocksLoaderWrapper;
+use super::{
+ address_cache_provider::SwiftAddressCacheWrapper,
+ shadowsocks_loader::SwiftShadowsocksLoaderWrapper,
+};
#[derive(Debug)]
pub struct SwiftAccessMethodResolver {
@@ -18,6 +21,7 @@ pub struct SwiftAccessMethodResolver {
domain: String,
state: EncryptedDnsProxyState,
bridge_provider: SwiftShadowsocksLoaderWrapper,
+ address_cache: SwiftAddressCacheWrapper,
}
impl SwiftAccessMethodResolver {
@@ -26,12 +30,14 @@ impl SwiftAccessMethodResolver {
domain: String,
state: EncryptedDnsProxyState,
bridge_provider: SwiftShadowsocksLoaderWrapper,
+ address_cache: SwiftAddressCacheWrapper,
) -> Self {
Self {
endpoint,
domain,
state,
bridge_provider,
+ address_cache,
}
}
}
@@ -79,8 +85,9 @@ impl AccessMethodResolver for SwiftAccessMethodResolver {
}
async fn default_connection_mode(&self) -> AllowedEndpoint {
- // TODO: Call the iOS Address cache implementation instead of returning the default endpoint
- let endpoint = ApiConnectionMode::Direct.get_endpoint().unwrap();
+ let endpoint =
+ Endpoint::from_socket_address(self.address_cache.get_addrs(), TransportProtocol::Tcp);
+
AllowedEndpoint {
endpoint,
clients: AllowedClients::All,
diff --git a/mullvad-ios/src/api_client/address_cache_provider.rs b/mullvad-ios/src/api_client/address_cache_provider.rs
new file mode 100644
index 0000000000..ff9f4a13e8
--- /dev/null
+++ b/mullvad-ios/src/api_client/address_cache_provider.rs
@@ -0,0 +1,68 @@
+use super::helpers::convert_c_string;
+use libc::c_char;
+use std::{ffi::c_void, net::SocketAddr};
+
+extern "C" {
+ /// Return the latest available endpoint, or a default one if none are cached
+ ///
+ /// # SAFETY
+ /// `rawAddressCacheProvider` **must** be provided by a call to `init_swift_address_cache_wrapper`
+ /// It is okay to persist it, and use it accross multiple threads.
+ pub fn swift_get_cached_endpoint(rawAddressCacheProvider: *const c_void) -> *const c_char;
+}
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct SwiftAddressCacheWrapper(SwiftAddressCacheProviderContext);
+
+impl SwiftAddressCacheWrapper {
+ pub fn new(context: SwiftAddressCacheProviderContext) -> SwiftAddressCacheWrapper {
+ SwiftAddressCacheWrapper(context)
+ }
+
+ pub fn get_addrs(&self) -> SocketAddr {
+ self.context_ref().get_addrs()
+ }
+
+ fn context_ref(&self) -> &SwiftAddressCacheProviderContext {
+ &self.0
+ }
+}
+
+// SAFETY: The `address_cache` inside the `SwiftAddressCacheProviderContext`'s context is guaranteed to be thread safe
+unsafe impl Sync for SwiftAddressCacheProviderContext {}
+// SAFETY: The `address_cache` inside the `SwiftAddressCacheProviderContext`'s context is guaranteed to be Sendable
+unsafe impl Send for SwiftAddressCacheProviderContext {}
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct SwiftAddressCacheProviderContext {
+ address_cache: *const c_void,
+}
+
+impl SwiftAddressCacheProviderContext {
+ pub fn get_addrs(&self) -> SocketAddr {
+ // SAFETY: See notice for `swift_get_cached_endpoint`
+ let raw_address = unsafe { swift_get_cached_endpoint(self.address_cache) };
+
+ // SAFETY: The pointer returned by `swift_get_cached_endpoint` is guaranteed to point to a valid UTF-8 String
+ // It is also guaranteed to be a valid representation of either an IPv4 or IPv6 address
+ unsafe { convert_c_string(raw_address) }
+ .parse()
+ .expect("Invalid socket address in cache")
+ }
+}
+
+/// Called by the Swift side in order to provide an object to rust that provides API addresses in a UTF-8 string form
+///
+/// # SAFETY
+/// `address_cache` **must be** pointing to a valid instance of a `DefaultAddressCacheProvider`
+/// That instance's lifetime has to be equivalent to a `'static` lifetime in Rust
+/// This function does not take ownership of `address_cache`
+#[unsafe(no_mangle)]
+pub unsafe extern "C" fn init_swift_address_cache_wrapper(
+ address_cache: *const c_void,
+) -> SwiftAddressCacheWrapper {
+ let context = SwiftAddressCacheProviderContext { address_cache };
+ SwiftAddressCacheWrapper::new(context)
+}
diff --git a/mullvad-ios/src/api_client/mod.rs b/mullvad-ios/src/api_client/mod.rs
index c65fe524a0..4e875fe4c6 100644
--- a/mullvad-ios/src/api_client/mod.rs
+++ b/mullvad-ios/src/api_client/mod.rs
@@ -2,6 +2,7 @@ use std::{ffi::c_char, ffi::CStr, future::Future, sync::Arc};
use access_method_resolver::SwiftAccessMethodResolver;
use access_method_settings::SwiftAccessMethodSettingsWrapper;
+use address_cache_provider::SwiftAddressCacheWrapper;
use helpers::convert_c_string;
use mullvad_api::{
access_mode::{AccessModeSelector, AccessModeSelectorHandle},
@@ -18,6 +19,7 @@ use talpid_future::retry::retry_future;
mod access_method_resolver;
mod access_method_settings;
mod account;
+mod address_cache_provider;
mod api;
mod cancellation;
mod completion;
@@ -135,6 +137,7 @@ pub extern "C" fn mullvad_api_init_new_tls_disabled(
domain: *const c_char,
bridge_provider: SwiftShadowsocksLoaderWrapper,
settings_provider: SwiftAccessMethodSettingsWrapper,
+ address_cache: SwiftAddressCacheWrapper,
) -> SwiftApiContext {
mullvad_api_init_inner(
host,
@@ -143,6 +146,7 @@ pub extern "C" fn mullvad_api_init_new_tls_disabled(
true,
bridge_provider,
settings_provider,
+ address_cache,
)
}
@@ -165,6 +169,7 @@ pub extern "C" fn mullvad_api_init_new(
domain: *const c_char,
bridge_provider: SwiftShadowsocksLoaderWrapper,
settings_provider: SwiftAccessMethodSettingsWrapper,
+ address_cache: SwiftAddressCacheWrapper,
) -> SwiftApiContext {
#[cfg(feature = "api-override")]
return mullvad_api_init_inner(
@@ -174,9 +179,17 @@ pub extern "C" fn mullvad_api_init_new(
false,
bridge_provider,
settings_provider,
+ address_cache,
);
#[cfg(not(feature = "api-override"))]
- mullvad_api_init_inner(host, address, domain, bridge_provider, settings_provider)
+ mullvad_api_init_inner(
+ host,
+ address,
+ domain,
+ bridge_provider,
+ settings_provider,
+ address_cache,
+ )
}
/// # Safety
@@ -199,6 +212,7 @@ pub extern "C" fn mullvad_api_init_inner(
#[cfg(feature = "api-override")] disable_tls: bool,
bridge_provider: SwiftShadowsocksLoaderWrapper,
settings_provider: SwiftAccessMethodSettingsWrapper,
+ address_cache: SwiftAddressCacheWrapper,
) -> SwiftApiContext {
// Safety: See notes for `convert_c_string`
let (host, address, domain) = unsafe {
@@ -233,6 +247,7 @@ pub extern "C" fn mullvad_api_init_inner(
domain,
encrypted_dns_proxy_state,
bridge_provider,
+ address_cache,
);
let api_context = tokio_handle.clone().block_on(async move {