diff options
| author | Bug Magnet <marco.nikic@mullvad.net> | 2025-06-04 11:39:14 +0200 |
|---|---|---|
| committer | Bug Magnet <marco.nikic@mullvad.net> | 2025-06-04 11:39:14 +0200 |
| commit | 1e42ad237f5fbb68027f0c3aefcf3b35c5b87500 (patch) | |
| tree | cc871d6c0e360d910ebda7c6f05b1d71ad5d1216 | |
| parent | 64862490c2781e4be3b8e77d5c297198fcb63d54 (diff) | |
| parent | e3ccff9b8313e5a13ce526c5b9cb531bd82f623c (diff) | |
| download | mullvadvpn-1e42ad237f5fbb68027f0c3aefcf3b35c5b87500.tar.xz mullvadvpn-1e42ad237f5fbb68027f0c3aefcf3b35c5b87500.zip | |
Merge branch 'move-api-access-method-test-call-to-mullvad-api-ios-1195'
14 files changed, 288 insertions, 93 deletions
diff --git a/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift b/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift index fa0bdc0ac7..6d1fcff990 100644 --- a/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift +++ b/ios/MullvadMockData/MullvadREST/APIProxy+Stubs.swift @@ -64,17 +64,25 @@ struct APIProxyStub: APIQuerying { func initStorekitPayment( accountNumber: String, - retryStrategy: MullvadREST.REST.RetryStrategy, - completionHandler: @escaping MullvadREST.ProxyCompletionHandler<String> + retryStrategy: REST.RetryStrategy, + completionHandler: @escaping ProxyCompletionHandler<String> ) -> any MullvadTypes.Cancellable { AnyCancellable() } func checkStorekitPayment( accountNumber: String, - transaction: MullvadTypes.StorekitTransaction, - retryStrategy: MullvadREST.REST.RetryStrategy, - completionHandler: @escaping MullvadREST.ProxyCompletionHandler<Void> + transaction: StorekitTransaction, + retryStrategy: REST.RetryStrategy, + completionHandler: @escaping ProxyCompletionHandler<Void> + ) -> any MullvadTypes.Cancellable { + AnyCancellable() + } + + func checkApiAvailability( + retryStrategy: REST.RetryStrategy, + accessMethod: PersistentAccessMethod, + completion: @escaping ProxyCompletionHandler<Bool> ) -> any MullvadTypes.Cancellable { AnyCancellable() } diff --git a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift index 8a74f8f6be..943c0afc3d 100644 --- a/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift +++ b/ios/MullvadREST/ApiHandlers/RESTAPIProxy.swift @@ -257,7 +257,7 @@ extension REST { request: LegacyStorekitRequest, retryStrategy: REST.RetryStrategy, completionHandler: @escaping ProxyCompletionHandler<REST.CreateApplePaymentResponse> - ) -> any MullvadTypes.Cancellable { + ) -> any Cancellable { AnyCancellable() } @@ -266,17 +266,25 @@ extension REST { accountNumber: String, retryStrategy: REST.RetryStrategy, completionHandler: @escaping ProxyCompletionHandler<String> - ) -> any MullvadTypes.Cancellable { + ) -> any Cancellable { AnyCancellable() } /// Not implemented. Use `MullvadAPIProxy` instead. public func checkStorekitPayment( accountNumber: String, - transaction: MullvadTypes.StorekitTransaction, + transaction: StorekitTransaction, retryStrategy: REST.RetryStrategy, completionHandler: @escaping ProxyCompletionHandler<Void> - ) -> any MullvadTypes.Cancellable { + ) -> any Cancellable { + AnyCancellable() + } + + public func checkApiAvailability( + retryStrategy: REST.RetryStrategy, + accessMethod: PersistentAccessMethod, + completion: @escaping ProxyCompletionHandler<Bool> + ) -> any Cancellable { AnyCancellable() } } diff --git a/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift b/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift index 6f270bf607..91f8888f91 100644 --- a/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift +++ b/ios/MullvadREST/MullvadAPI/APIHandlers/MullvadAPIProxy.swift @@ -60,6 +60,12 @@ public protocol APIQuerying: Sendable { retryStrategy: REST.RetryStrategy, completionHandler: @escaping @Sendable ProxyCompletionHandler<Void> ) -> Cancellable + + func checkApiAvailability( + retryStrategy: REST.RetryStrategy, + accessMethod: PersistentAccessMethod, + completion: @escaping @Sendable ProxyCompletionHandler<Bool> + ) -> Cancellable } extension REST { @@ -159,6 +165,24 @@ extension REST { }) } + public func checkApiAvailability( + retryStrategy: REST.RetryStrategy, + accessMethod: PersistentAccessMethod, + completion: @escaping @Sendable ProxyCompletionHandler<Bool> + ) -> Cancellable { + let responseHandler = rustEmptyResponseHandler() + return createNetworkOperation( + request: .checkApiAvailability(retryStrategy, accessMethod: accessMethod), + responseHandler: responseHandler + ) { result in + if case let .failure(err) = result { + completion(.failure(err)) + } else { + completion(.success(true)) + } + } + } + public func legacyStorekitPayment( accountNumber: String, request: LegacyStorekitRequest, diff --git a/ios/MullvadREST/MullvadAPI/APIRequest/APIRequest.swift b/ios/MullvadREST/MullvadAPI/APIRequest/APIRequest.swift index 5f27695672..e9ea133034 100644 --- a/ios/MullvadREST/MullvadAPI/APIRequest/APIRequest.swift +++ b/ios/MullvadREST/MullvadAPI/APIRequest/APIRequest.swift @@ -13,6 +13,7 @@ public enum APIRequest: Codable, Sendable { case getAddressList(_ retryStrategy: REST.RetryStrategy) case getRelayList(_ retryStrategy: REST.RetryStrategy, etag: String?) case sendProblemReport(_ retryStrategy: REST.RetryStrategy, problemReportRequest: ProblemReportRequest) + case checkApiAvailability(_ retryStrategy: REST.RetryStrategy, accessMethod: PersistentAccessMethod) // Account Proxy case createAccount(_ retryStrategy: REST.RetryStrategy) @@ -72,6 +73,8 @@ public enum APIRequest: Codable, Sendable { "init-storekit-payment" case .checkStorekitPayment: "check-storekit-payment" + case .checkApiAvailability: + "check-api-availability" } } @@ -90,7 +93,8 @@ public enum APIRequest: Codable, Sendable { let .rotateDeviceKey(strategy, _, _, _), let .legacyStorekitPayment(strategy, _, _), let .initStorekitPayment(strategy, _), - let .checkStorekitPayment(strategy, _, _): + let .checkStorekitPayment(strategy, _, _), + let .checkApiAvailability(strategy, _): strategy } } diff --git a/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift index b7e6c8412f..03d430a3a7 100644 --- a/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift +++ b/ios/MullvadREST/MullvadAPI/MullvadApiRequestFactory.swift @@ -114,6 +114,13 @@ public struct MullvadApiRequestFactory: Sendable { accountNumber, request.publicKey.rawValue.map { $0 } )) + case let .checkApiAvailability(retryStrategy, accessMethod): + return MullvadApiCancellable(handle: mullvad_ios_api_addrs_available( + apiContext.context, + rawCompletionPointer, + retryStrategy.toRustStrategy(), + convertAccessMethod(accessMethod: accessMethod) + )) case let .legacyStorekitPayment( retryStrategy: retryStrategy, accountNumber: accountNumber, diff --git a/ios/MullvadRustRuntime/MullvadConnectionModeProvider.swift b/ios/MullvadRustRuntime/MullvadConnectionModeProvider.swift index f7d2e0238f..0504fb06c8 100644 --- a/ios/MullvadRustRuntime/MullvadConnectionModeProvider.swift +++ b/ios/MullvadRustRuntime/MullvadConnectionModeProvider.swift @@ -8,7 +8,6 @@ import MullvadTypes -// swiftlint:disable:next function_body_length public func initAccessMethodSettingsWrapper(methods: [PersistentAccessMethod]) -> SwiftAccessMethodSettingsWrapper { // 1. Get all the built in access methods, it is expected that they are always available @@ -21,67 +20,15 @@ public func initAccessMethodSettingsWrapper(methods: [PersistentAccessMethod]) let customMethods = methods.filter { defaultMethods.contains($0.proxyConfiguration) == false } // 3. Convert the builtin access methods - let directMethodRaw = convert_builtin_access_method_setting( - directMethod.id.uuidString, - directMethod.name, - directMethod.isEnabled, - UInt8(KindDirect.rawValue), - nil - ) - let bridgesMethodRaw = convert_builtin_access_method_setting( - bridgesMethod.id.uuidString, - bridgesMethod.name, - bridgesMethod.isEnabled, - UInt8(KindBridge.rawValue), - nil - ) - let encryptedDNSMethodRaw = convert_builtin_access_method_setting( - encryptedDNSMethod.id.uuidString, - encryptedDNSMethod.name, - encryptedDNSMethod.isEnabled, - UInt8(KindEncryptedDnsProxy.rawValue), - nil - ) + let directMethodRaw = convertAccessMethod(accessMethod: directMethod) + let bridgesMethodRaw = convertAccessMethod(accessMethod: bridgesMethod) + let encryptedDNSMethodRaw = convertAccessMethod(accessMethod: encryptedDNSMethod) var rawCustomMethods = ContiguousArray<UnsafeRawPointer?>([]) // 4. Convert the custom access methods (all takes different parameters) for method in customMethods { - if case let .shadowsocks(config) = method.proxyConfiguration { - let serverAddress = config.server.rawValue.map { $0 } - let shadowsocksConfiguration = new_shadowsocks_access_method_setting( - serverAddress, - UInt(serverAddress.count), - config.port, - config.password, - config.cipher.rawValue.rawValue - ) - let shadowsocksMethodRaw = convert_builtin_access_method_setting( - method.id.uuidString, - method.name, - method.isEnabled, - UInt8(KindShadowsocks.rawValue), - shadowsocksConfiguration - ) - rawCustomMethods.append(shadowsocksMethodRaw) - } - if case let .socks5(config) = method.proxyConfiguration { - let serverAddress = config.server.rawValue.map { $0 } - let socks5Configuration = new_socks5_access_method_setting( - serverAddress, - UInt(serverAddress.count), - config.port, - config.credential?.username, - config.credential?.password - ) - let socks5MethodRaw = convert_builtin_access_method_setting( - method.id.uuidString, - method.name, - method.isEnabled, - UInt8(KindSocks5Local.rawValue), - socks5Configuration - ) - rawCustomMethods.append(socks5MethodRaw) - } + let rawMethod = convertAccessMethod(accessMethod: method) + rawCustomMethods.append(rawMethod) } // 5. Reunite them all in one, and pass it to rust @@ -97,3 +44,63 @@ public func initAccessMethodSettingsWrapper(methods: [PersistentAccessMethod]) } ) } + +public func convertAccessMethod(accessMethod: PersistentAccessMethod) -> UnsafeMutableRawPointer? { + switch accessMethod.proxyConfiguration { + case .direct, .bridges, .encryptedDNS: + return convert_builtin_access_method_setting( + accessMethod.id.uuidString, + accessMethod.name, + accessMethod.isEnabled, + accessMethod.kind(), + nil + ) + case let .shadowsocks(configuration): + let serverAddress = configuration.server.rawValue.map { $0 } + let shadowsocksConfiguration = new_shadowsocks_access_method_setting( + serverAddress, + UInt(serverAddress.count), + configuration.port, + configuration.password, + configuration.cipher.rawValue.rawValue + ) + let shadowsocksMethodRaw = convert_builtin_access_method_setting( + accessMethod.id.uuidString, + accessMethod.name, + accessMethod.isEnabled, + accessMethod.kind(), + shadowsocksConfiguration + ) + return shadowsocksMethodRaw + case let .socks5(configuration): + let serverAddress = configuration.server.rawValue.map { $0 } + let socks5Configuration = new_socks5_access_method_setting( + serverAddress, + UInt(serverAddress.count), + configuration.port, + configuration.credential?.username, + configuration.credential?.password + ) + let socks5MethodRaw = convert_builtin_access_method_setting( + accessMethod.id.uuidString, + accessMethod.name, + accessMethod.isEnabled, + accessMethod.kind(), + socks5Configuration + ) + return socks5MethodRaw + } +} + +fileprivate +extension PersistentAccessMethod { + func kind() -> UInt8 { + switch kind { + case .direct: UInt8(KindDirect.rawValue) + case .bridges: UInt8(KindBridge.rawValue) + case .encryptedDNS: UInt8(KindEncryptedDnsProxy.rawValue) + case .shadowsocks: UInt8(KindShadowsocks.rawValue) + case .socks5: UInt8(KindSocks5Local.rawValue) + } + } +} diff --git a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h index 08bb177116..d4c0bf334f 100644 --- a/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h +++ b/ios/MullvadRustRuntime/include/mullvad_rust_runtime.h @@ -89,10 +89,10 @@ typedef struct LateStringDeallocator { typedef struct SwiftMullvadApiResponse { uint8_t *body; uintptr_t body_size; - uint8_t *etag; + char *etag; uint16_t status_code; - uint8_t *error_description; - uint8_t *server_response_code; + char *error_description; + char *server_response_code; bool success; } SwiftMullvadApiResponse; @@ -366,6 +366,26 @@ struct SwiftCancelHandle mullvad_ios_get_addresses(struct SwiftApiContext api_co * object `MullvadApiCompletion`. The pointer will be freed by calling `mullvad_api_completion_finish` * when completion finishes (in completion.finish). * + * `retry_strategy` must have been created by a call to either of the following functions + * `mullvad_api_retry_strategy_never`, `mullvad_api_retry_strategy_constant` or `mullvad_api_retry_strategy_exponential` + * + * This function is not safe to call multiple times with the same `CompletionCookie`. + */ +struct SwiftCancelHandle mullvad_ios_api_addrs_available(struct SwiftApiContext api_context, + void *completion_cookie, + struct SwiftRetryStrategy retry_strategy, + const void *access_method_setting); + +/** + * # Safety + * + * `api_context` must be pointing to a valid instance of `SwiftApiContext`. A `SwiftApiContext` is created + * by calling `mullvad_api_init_new`. + * + * This function takes ownership of `completion_cookie`, which must be pointing to a valid instance of Swift + * object `MullvadApiCompletion`. The pointer will be freed by calling `mullvad_api_completion_finish` + * when completion finishes (in completion.finish). + * * `etag` must be a pointer to a null terminated string. * * `retry_strategy` must have been created by a call to either of the following functions diff --git a/ios/MullvadTypes/PersistentAccessMethod.swift b/ios/MullvadTypes/PersistentAccessMethod.swift index 8b9d6d57ae..9a3310d06f 100644 --- a/ios/MullvadTypes/PersistentAccessMethod.swift +++ b/ios/MullvadTypes/PersistentAccessMethod.swift @@ -24,7 +24,7 @@ public struct PersistentAccessMethodStore: Codable { } /// Persistent access method model. -public struct PersistentAccessMethod: Identifiable, Codable, Equatable { +public struct PersistentAccessMethod: Identifiable, Codable, Equatable, Sendable { /// The unique identifier used for referencing the access method entry in a persistent store. public var id: UUID @@ -63,7 +63,7 @@ public struct PersistentAccessMethod: Identifiable, Codable, Equatable { } /// Persistent proxy configuration. -public enum PersistentProxyConfiguration: Codable, Equatable { +public enum PersistentProxyConfiguration: Codable, Equatable, Sendable { /// Direct communication without proxy. case direct @@ -98,7 +98,7 @@ extension PersistentProxyConfiguration { } /// Socks v5 proxy configuration. - public struct SocksConfiguration: Codable, Equatable { + public struct SocksConfiguration: Codable, Equatable, Sendable { /// Proxy server address. public var server: AnyIPAddress @@ -132,7 +132,7 @@ extension PersistentProxyConfiguration { } /// Shadowsocks configuration. - public struct ShadowsocksConfiguration: Codable, Equatable { + public struct ShadowsocksConfiguration: Codable, Equatable, Sendable { /// Server address. public var server: AnyIPAddress diff --git a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift index de0a0b2551..8e194bd74b 100644 --- a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift +++ b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift @@ -17,14 +17,24 @@ class ProxyConfigurationTester: ProxyConfigurationTesterProtocol { private var cancellable: MullvadTypes.Cancellable? private let transportProvider: ProxyConfigurationTransportProvider private var headRequest: REST.APIAvailabilityTestRequest? + private let apiProxy: APIQuerying - init(transportProvider: ProxyConfigurationTransportProvider) { + init(transportProvider: ProxyConfigurationTransportProvider, apiProxy: APIQuerying) { self.transportProvider = transportProvider + self.apiProxy = apiProxy } - func start(configuration: PersistentProxyConfiguration, completion: @escaping @Sendable (Error?) -> Void) { + func start(configuration: PersistentAccessMethod, completion: @escaping @Sendable (Error?) -> Void) { + #if DEBUG + cancellable = apiProxy.checkApiAvailability(retryStrategy: .noRetry, accessMethod: configuration) { success in + switch success { + case .success: completion(nil) + case let .failure(error): completion(error) + } + } + #else do { - let transport = try transportProvider.makeTransport(with: configuration) + let transport = try transportProvider.makeTransport(with: configuration.proxyConfiguration) let request = REST.APIAvailabilityTestRequest(transport: transport) headRequest = request cancellable = request.makeRequest { error in @@ -35,6 +45,7 @@ class ProxyConfigurationTester: ProxyConfigurationTesterProtocol { } catch { completion(error) } + #endif } func cancel() { diff --git a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift index 6ebc7aa655..66b31f9193 100644 --- a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift +++ b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift @@ -15,7 +15,7 @@ protocol ProxyConfigurationTesterProtocol { /// - Parameters: /// - configuration: a proxy configuration. /// - completion: a completion handler that receives `nil` upon success, otherwise the underlying error. - func start(configuration: PersistentProxyConfiguration, completion: @escaping @Sendable (Error?) -> Void) + func start(configuration: PersistentAccessMethod, completion: @escaping @Sendable (Error?) -> Void) /// Cancel testing proxy configuration. func cancel() diff --git a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift index 7aa56ed588..87a2cbf24e 100644 --- a/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/ApplicationCoordinator.swift @@ -570,7 +570,10 @@ final class ApplicationCoordinator: Coordinator, Presenting, @preconcurrency Roo let navigationController = CustomNavigationController() navigationController.view.setAccessibilityIdentifier(.settingsContainerView) - let configurationTester = ProxyConfigurationTester(transportProvider: configuredTransportProvider) + let configurationTester = ProxyConfigurationTester( + transportProvider: configuredTransportProvider, + apiProxy: apiProxy + ) let coordinator = SettingsCoordinator( navigationController: navigationController, diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift index 7770c3b2e1..ac7a9125ff 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift @@ -52,7 +52,7 @@ struct EditAccessMethodInteractor: EditAccessMethodInteractorProtocol { } func startProxyConfigurationTest(_ completion: (@Sendable (Bool) -> Void)?) { - guard let config = try? subject.value.intoPersistentProxyConfiguration() else { return } + guard let config = try? subject.value.intoPersistentAccessMethod() else { return } let subject = subject subject.value.testingStatus = .inProgress diff --git a/mullvad-ios/src/api_client/api.rs b/mullvad-ios/src/api_client/api.rs index 6578be1d14..14c36bc595 100644 --- a/mullvad-ios/src/api_client/api.rs +++ b/mullvad-ios/src/api_client/api.rs @@ -1,16 +1,18 @@ -use std::ffi::CStr; +use std::ffi::{c_void, CStr}; use std::os::raw::c_char; use mullvad_api::{ rest::{self, MullvadRestHandle}, ApiProxy, RelayListProxy, }; +use mullvad_types::access_method::AccessMethodSetting; use super::{ cancellation::{RequestCancelHandle, SwiftCancelHandle}, completion::{CompletionCookie, SwiftCompletionHandler}, do_request, response::SwiftMullvadApiResponse, + retry_request, retry_strategy::{RetryStrategy, SwiftRetryStrategy}, SwiftApiContext, }; @@ -68,6 +70,78 @@ pub unsafe extern "C" fn mullvad_ios_get_addresses( /// object `MullvadApiCompletion`. The pointer will be freed by calling `mullvad_api_completion_finish` /// when completion finishes (in completion.finish). /// +/// `retry_strategy` must have been created by a call to either of the following functions +/// `mullvad_api_retry_strategy_never`, `mullvad_api_retry_strategy_constant` or `mullvad_api_retry_strategy_exponential` +/// +/// This function is not safe to call multiple times with the same `CompletionCookie`. +#[no_mangle] +pub unsafe extern "C" fn mullvad_ios_api_addrs_available( + api_context: SwiftApiContext, + completion_cookie: *mut libc::c_void, + retry_strategy: SwiftRetryStrategy, + access_method_setting: *const c_void, +) -> SwiftCancelHandle { + let completion_handler = SwiftCompletionHandler::new(CompletionCookie::new(completion_cookie)); + + let Ok(tokio_handle) = crate::mullvad_ios_runtime() else { + completion_handler.finish(SwiftMullvadApiResponse::no_tokio_runtime()); + return SwiftCancelHandle::empty(); + }; + + let api_context = api_context.rust_context(); + // SAFETY: See notes for `into_rust` + let retry_strategy = unsafe { retry_strategy.into_rust() }; + let completion = completion_handler.clone(); + // SAFETY: `access_method_setting` must be a raw pointer resulting from a call to `convert_builtin_access_method_setting` + let access_method_setting: AccessMethodSetting = + unsafe { *Box::from_raw(access_method_setting as *mut _) }; + + let task = tokio_handle.clone().spawn(async move { + match api_context + .access_mode_handler + .resolve(access_method_setting.clone()) + .await + { + Ok(Some(resolved_connection_mode)) => { + let oneshot_client = api_context + .api_client + .mullvad_rest_handle(resolved_connection_mode.connection_mode.into_provider()); + + match mullvad_ios_api_addrs_available_inner(oneshot_client, retry_strategy).await { + Ok(_) => completion.finish(SwiftMullvadApiResponse::ok()), + Err(err) => { + log::error!("{err:?}"); + completion.finish(SwiftMullvadApiResponse::rest_error(err)); + } + } + } + Ok(None) => { + log::error!("Invalid access method configuration, {access_method_setting:?}"); + completion.finish(SwiftMullvadApiResponse::access_method_error( + mullvad_api::access_mode::Error::Resolve { + access_method: access_method_setting.access_method, + }, + )); + } + Err(err) => { + log::error!("{err:?}"); + completion.finish(SwiftMullvadApiResponse::access_method_error(err)); + } + } + }); + + RequestCancelHandle::new(task, completion_handler.clone()).into_swift() +} + +/// # Safety +/// +/// `api_context` must be pointing to a valid instance of `SwiftApiContext`. A `SwiftApiContext` is created +/// by calling `mullvad_api_init_new`. +/// +/// This function takes ownership of `completion_cookie`, which must be pointing to a valid instance of Swift +/// object `MullvadApiCompletion`. The pointer will be freed by calling `mullvad_api_completion_finish` +/// when completion finishes (in completion.finish). +/// /// `etag` must be a pointer to a null terminated string. /// /// `retry_strategy` must have been created by a call to either of the following functions @@ -137,3 +211,13 @@ async fn mullvad_ios_get_relays_inner( do_request(retry_strategy, future_factory).await } + +async fn mullvad_ios_api_addrs_available_inner( + rest_client: MullvadRestHandle, + retry_strategy: RetryStrategy, +) -> Result<bool, rest::Error> { + let api = ApiProxy::new(rest_client); + + let future_factory = || api.api_addrs_available(); + retry_request(retry_strategy, future_factory).await +} diff --git a/mullvad-ios/src/api_client/response.rs b/mullvad-ios/src/api_client/response.rs index 3b017e37c6..4871a5d4bf 100644 --- a/mullvad-ios/src/api_client/response.rs +++ b/mullvad-ios/src/api_client/response.rs @@ -1,5 +1,5 @@ use std::{ - ffi::CString, + ffi::{c_char, CString}, ptr::{self, null_mut}, }; @@ -12,10 +12,10 @@ use mullvad_api::{ pub struct SwiftMullvadApiResponse { body: *mut u8, body_size: usize, - etag: *mut u8, + etag: *mut c_char, status_code: u16, - error_description: *mut u8, - server_response_code: *mut u8, + error_description: *mut c_char, + server_response_code: *mut c_char, success: bool, } @@ -33,7 +33,7 @@ impl SwiftMullvadApiResponse { Some(etag) => { let header_value = CString::new(etag).map_err(|_| rest::Error::InvalidHeaderError)?; - header_value.into_raw().cast() + header_value.into_raw() } None => ptr::null_mut(), }; @@ -61,6 +61,25 @@ impl SwiftMullvadApiResponse { } } + pub fn access_method_error(err: mullvad_api::access_mode::Error) -> Self { + let to_cstr_pointer = |str| { + CString::new(str) + .map(|cstr| cstr.into_raw()) + .unwrap_or(null_mut()) + }; + let error_description = to_cstr_pointer(err.to_string()); + + Self { + body: null_mut(), + body_size: 0, + etag: null_mut(), + status_code: StatusCode::INTERNAL_SERVER_ERROR.as_u16(), + error_description, + server_response_code: null_mut(), + success: false, + } + } + pub fn rest_error(err: mullvad_api::rest::Error) -> Self { if err.is_aborted() { return Self::cancelled(); @@ -68,7 +87,7 @@ impl SwiftMullvadApiResponse { let to_cstr_pointer = |str| { CString::new(str) - .map(|cstr| cstr.into_raw().cast()) + .map(|cstr| cstr.into_raw()) .unwrap_or(null_mut()) }; @@ -94,7 +113,7 @@ impl SwiftMullvadApiResponse { pub fn cancelled() -> Self { Self { success: false, - error_description: c"Request was cancelled".to_owned().into_raw().cast(), + error_description: c"Request was cancelled".to_owned().into_raw(), body: null_mut(), body_size: 0, etag: null_mut(), @@ -106,7 +125,7 @@ impl SwiftMullvadApiResponse { pub fn no_tokio_runtime() -> Self { Self { success: false, - error_description: c"Failed to get Tokio runtime".to_owned().into_raw().cast(), + error_description: c"Failed to get Tokio runtime".to_owned().into_raw(), body: null_mut(), body_size: 0, etag: null_mut(), @@ -130,14 +149,14 @@ pub unsafe extern "C" fn mullvad_response_drop(response: SwiftMullvadApiResponse } if !response.etag.is_null() { - let _ = CString::from_raw(response.etag.cast()); + let _ = CString::from_raw(response.etag); } if !response.error_description.is_null() { - let _ = CString::from_raw(response.error_description.cast()); + let _ = CString::from_raw(response.error_description); } if !response.server_response_code.is_null() { - let _ = CString::from_raw(response.server_response_code.cast()); + let _ = CString::from_raw(response.server_response_code); } } |
