diff --git a/Cargo.lock b/Cargo.lock index c215b8657e40..f42c68dc7e30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2898,7 +2898,6 @@ version = "0.0.0" dependencies = [ "anyhow", "async-trait", - "cbindgen", "chrono", "clap", "futures", @@ -2909,7 +2908,6 @@ dependencies = [ "ipnetwork", "libc", "log", - "mockito", "mullvad-api-constants", "mullvad-encrypted-dns-proxy", "mullvad-fs", @@ -2928,7 +2926,6 @@ dependencies = [ "tokio-socks", "tower", "tracing-subscriber", - "uuid", "vec1", "webpki-roots 1.0.4", ] diff --git a/ios/MullvadREST/MullvadAPI/MullvadApiNetworkOperation.swift b/ios/MullvadREST/MullvadAPI/MullvadApiNetworkOperation.swift index 1d8352c920ea..5b11d3c03327 100644 --- a/ios/MullvadREST/MullvadAPI/MullvadApiNetworkOperation.swift +++ b/ios/MullvadREST/MullvadAPI/MullvadApiNetworkOperation.swift @@ -79,7 +79,10 @@ extension REST { logger.debug("\(#function): \(request.name) API response received") if let apiError = response.error { - logger.error("Request failed to send error=\(apiError)") + logger + .error( + "Response contained error code \(apiError.statusCode), error: \(apiError.errorDescription)" + ) finish(result: .failure(restError(apiError: apiError))) return } diff --git a/ios/MullvadRustRuntime/MullvadAddressCacheKeychainStore.swift b/ios/MullvadRustRuntime/MullvadAddressCacheKeychainStore.swift index 755b62cc0612..c9aa70cad8ec 100644 --- a/ios/MullvadRustRuntime/MullvadAddressCacheKeychainStore.swift +++ b/ios/MullvadRustRuntime/MullvadAddressCacheKeychainStore.swift @@ -8,9 +8,15 @@ import MullvadSettings +/// Whether the settings store is available. It requires `ApplicationSecurityGroupIdentifier` +/// to be present in the main bundle's Info.plist, which is not the case in e.g. UI test runners. +private let isSettingsStoreAvailable: Bool = Bundle.main + .object(forInfoDictionaryKey: "ApplicationSecurityGroupIdentifier") != nil + /// Store the address cache, given to us by the Rust code, to the keychain @_cdecl("swift_store_address_cache") func storeAddressCache(_ pointer: UnsafeRawPointer, dataSize: UInt64) { + guard isSettingsStoreAvailable else { return } let data = Data(bytes: pointer, count: Int(dataSize)) // if writing to the Keychain fails, it will do so silently. try? SettingsManager.writeAddressCache(data) @@ -18,6 +24,7 @@ func storeAddressCache(_ pointer: UnsafeRawPointer, dataSize: UInt64) { @_cdecl("swift_read_address_cache") func readAddressCache() -> SwiftData { + guard isSettingsStoreAvailable else { return SwiftData(data: NSData()) } let data = (try? SettingsManager.readAddressCache()) ?? Data() return SwiftData(data: data as NSData) } diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index f2d1a6a82acc..8423f918af83 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -7,6 +7,20 @@ objects = { /* Begin PBXBuildFile section */ + 0107F40B2F5B02580012451B /* WireGuardKitTypes in Frameworks */ = {isa = PBXBuildFile; productRef = 0107F40A2F5B02580012451B /* WireGuardKitTypes */; }; + 0107F40D2F5B02840012451B /* MullvadTypes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; }; + 0107F4142F5B02D70012451B /* MullvadLogging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */; }; + 0107F4152F5B02D70012451B /* MullvadLogging.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0107F4172F5B02D80012451B /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; }; + 0107F4182F5B02D80012451B /* MullvadREST.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0107F4192F5B02D90012451B /* MullvadRustRuntime.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A992DA1D2C24709F00DE7CE5 /* MullvadRustRuntime.framework */; }; + 0107F41A2F5B02D90012451B /* MullvadRustRuntime.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A992DA1D2C24709F00DE7CE5 /* MullvadRustRuntime.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0107F41B2F5B02DA0012451B /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; }; + 0107F41C2F5B02DA0012451B /* MullvadSettings.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0107F41D2F5B02DB0012451B /* MullvadTypes.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223D5294C8E5E0029F5F8 /* MullvadTypes.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0107F41E2F5B02DC0012451B /* Operations.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223A5294C8A480029F5F8 /* Operations.framework */; }; + 0107F41F2F5B02DC0012451B /* Operations.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223A5294C8A480029F5F8 /* Operations.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0107F4202F5B02DE0012451B /* WireGuardKitTypes in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 0107F40A2F5B02580012451B /* WireGuardKitTypes */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 0107F4222F5B97F30012451B /* RelayListCacheTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0107F4212F5B97F30012451B /* RelayListCacheTests.swift */; }; 0107F42D2F5F62950012451B /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; }; 0107F4322F5F62C80012451B /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; }; @@ -20,7 +34,6 @@ 01C981EA2F43C3410002D284 /* BlockedStateReason+Localization.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C981E52F43C3410002D284 /* BlockedStateReason+Localization.swift */; }; 01C981EB2F45D5C20002D284 /* TunnelControlPageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01C981EA2F45D5C20002D284 /* TunnelControlPageTests.swift */; }; 01D13C212F11130500EC63DE /* RustLogging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01D13C202F11130500EC63DE /* RustLogging.swift */; }; - 01EF6F342B6A590700125696 /* libmullvad_api.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01EF6F332B6A590700125696 /* libmullvad_api.a */; }; 01FFF95F2F0BE13900BDAF45 /* SelectedEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FFF95E2F0BE13900BDAF45 /* SelectedEndpoint.swift */; }; 01FFF9612F0BE14E00BDAF45 /* ObfuscationMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01FFF9602F0BE14E00BDAF45 /* ObfuscationMethod.swift */; }; 062B45A328FD4CA700746E77 /* le_root_cert.cer in Resources */ = {isa = PBXBuildFile; fileRef = 06799AB428F98CE700ACD94E /* le_root_cert.cer */; }; @@ -728,7 +741,6 @@ 85557B202B5FBBD700795FE1 /* AccountPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85557B1F2B5FBBD700795FE1 /* AccountPage.swift */; }; 8555C6602D1030040092DAD0 /* LeakCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8555C65F2D102FFE0092DAD0 /* LeakCheck.swift */; }; 8556EB522B9A1C6900D26DD4 /* MullvadApi.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */; }; - 8556EB542B9A1D7100D26DD4 /* BridgingHeader.h in Headers */ = {isa = PBXBuildFile; fileRef = 8556EB532B9A1D7100D26DD4 /* BridgingHeader.h */; }; 8556EB562B9B0AC500D26DD4 /* RevokedDevicePage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */; }; 855D9F5B2B63E56B00D7C64D /* ProblemReportPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */; }; 85607C892D131CD500037E34 /* TestRouterAPIClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85607C882D131CCD00037E34 /* TestRouterAPIClient.swift */; }; @@ -740,7 +752,6 @@ 8590A5442C2AF43400B9BF7B /* TrafficGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590A5432C2AF43400B9BF7B /* TrafficGenerator.swift */; }; 85978A542BE0F10E00F999A7 /* PacketCapture.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85978A532BE0F10E00F999A7 /* PacketCapture.swift */; }; 85A42B882BB44D31007BABF7 /* DeviceManagementPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */; }; - 85B267612B849ADB0098E3CD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 85B267602B849ADB0098E3CD /* mullvad-api.h */; }; 85C7A2E92B89024B00035D5A /* SettingsLoggedOutTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85C7A2E82B89024B00035D5A /* SettingsLoggedOutTests.swift */; }; 85D039982BA4711800940E7F /* SettingsMigrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85D039972BA4711800940E7F /* SettingsMigrationTests.swift */; }; 85D2B0B12B6BD32400DF9DA7 /* BaseUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8590896A2B61763B003AF5F5 /* BaseUITestCase.swift */; }; @@ -900,7 +911,6 @@ A9D9A4CF2C36D54E004088DD /* TCPConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 585A02EC2A4B28F300C6CAFF /* TCPConnection.swift */; }; A9D9A4D22C36DBAF004088DD /* MullvadPostQuantum+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9C308392C19DDA7008715F1 /* MullvadPostQuantum+Stubs.swift */; }; A9D9A4D42C36E1EA004088DD /* mullvad_rust_runtime.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D9A4D32C36E1EA004088DD /* mullvad_rust_runtime.h */; settings = {ATTRIBUTES = (Private, ); }; }; - A9DF789B2B7D1DF10094E4AD /* mullvad-api.h in Headers */ = {isa = PBXBuildFile; fileRef = 01EF6F2D2B6A51B100125696 /* mullvad-api.h */; settings = {ATTRIBUTES = (Private, ); }; }; A9DF789D2B7D1E8B0094E4AD /* LoggedInWithTimeUITestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859089692B61763B003AF5F5 /* LoggedInWithTimeUITestCase.swift */; }; A9E031782ACB09930095D843 /* BackgroundTaskProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E031762ACB08950095D843 /* BackgroundTaskProvider.swift */; }; A9E0317A2ACB0AE70095D843 /* UIApplication+Stubs.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9E031792ACB0AE70095D843 /* UIApplication+Stubs.swift */; }; @@ -1151,6 +1161,48 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 0107F3FC2F5AE1E40012451B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 06799ABB28F98E1D00ACD94E; + remoteInfo = MullvadREST; + }; + 0107F4002F5AE2860012451B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58B2FDD22AA71D2A003EB5C6; + remoteInfo = MullvadSettings; + }; + 0107F4042F5B02310012451B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58D223D4294C8E5E0029F5F8; + remoteInfo = MullvadTypes; + }; + 0107F4082F5B024D0012451B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58D223A4294C8A480029F5F8; + remoteInfo = Operations; + }; + 0107F40E2F5B02840012451B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58D223D4294C8E5E0029F5F8; + remoteInfo = MullvadTypes; + }; + 0107F4122F5B02AD0012451B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 58D223F2294C8FF00029F5F8; + remoteInfo = MullvadLogging; + }; 0107F42F2F5F62950012451B /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58CE5E58224146200008646E /* Project object */; @@ -1438,6 +1490,13 @@ remoteGlobalIDString = 58CE5E5F224146200008646E; remoteInfo = MullvadVPN; }; + 856E0AD22B4D9C1F007EAD4C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 58CE5E58224146200008646E /* Project object */; + proxyType = 1; + remoteGlobalIDString = A992DA1C2C24709F00DE7CE5; + remoteInfo = MullvadRustRuntime; + }; A9173C332C36CCFB00F6A08C /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58CE5E58224146200008646E /* Project object */; @@ -1497,6 +1556,23 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 0107F4162F5B02D70012451B /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 0107F41A2F5B02D90012451B /* MullvadRustRuntime.framework in Embed Frameworks */, + 0107F4182F5B02D80012451B /* MullvadREST.framework in Embed Frameworks */, + 0107F41D2F5B02DB0012451B /* MullvadTypes.framework in Embed Frameworks */, + 0107F4202F5B02DE0012451B /* WireGuardKitTypes in Embed Frameworks */, + 0107F4152F5B02D70012451B /* MullvadLogging.framework in Embed Frameworks */, + 0107F41F2F5B02DC0012451B /* Operations.framework in Embed Frameworks */, + 0107F41C2F5B02DA0012451B /* MullvadSettings.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 06799AD628F98E1D00ACD94E /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -1632,8 +1708,6 @@ 01C981E62F43C3410002D284 /* TunnelStateAccessibilityAnnouncer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelStateAccessibilityAnnouncer.swift; sourceTree = ""; }; 01C981EA2F45D5C20002D284 /* TunnelControlPageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelControlPageTests.swift; sourceTree = ""; }; 01D13C202F11130500EC63DE /* RustLogging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RustLogging.swift; sourceTree = ""; }; - 01EF6F2D2B6A51B100125696 /* mullvad-api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "mullvad-api.h"; path = "../mullvad-api/include/mullvad-api.h"; sourceTree = ""; }; - 01EF6F332B6A590700125696 /* libmullvad_api.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmullvad_api.a; path = "../target/aarch64-apple-ios/debug/libmullvad_api.a"; sourceTree = ""; }; 01F1FF1D29F0627D007083C3 /* libmullvad_ios.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libmullvad_ios.a; path = ../target/debug/libmullvad_ios.a; sourceTree = ""; }; 01FFF95E2F0BE13900BDAF45 /* SelectedEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedEndpoint.swift; sourceTree = ""; }; 01FFF9602F0BE14E00BDAF45 /* ObfuscationMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObfuscationMethod.swift; sourceTree = ""; }; @@ -2307,7 +2381,6 @@ 85557B1F2B5FBBD700795FE1 /* AccountPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountPage.swift; sourceTree = ""; }; 8555C65F2D102FFE0092DAD0 /* LeakCheck.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LeakCheck.swift; sourceTree = ""; }; 8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = MullvadApi.swift; path = MullvadVPNUITests/MullvadApi.swift; sourceTree = ""; }; - 8556EB532B9A1D7100D26DD4 /* BridgingHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BridgingHeader.h; sourceTree = ""; }; 8556EB552B9B0AC500D26DD4 /* RevokedDevicePage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevokedDevicePage.swift; sourceTree = ""; }; 855D9F5A2B63E56B00D7C64D /* ProblemReportPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProblemReportPage.swift; sourceTree = ""; }; 85607C882D131CCD00037E34 /* TestRouterAPIClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestRouterAPIClient.swift; sourceTree = ""; }; @@ -2321,7 +2394,6 @@ 8590A5432C2AF43400B9BF7B /* TrafficGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrafficGenerator.swift; sourceTree = ""; }; 85978A532BE0F10E00F999A7 /* PacketCapture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketCapture.swift; sourceTree = ""; }; 85A42B872BB44D31007BABF7 /* DeviceManagementPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceManagementPage.swift; sourceTree = ""; }; - 85B267602B849ADB0098E3CD /* mullvad-api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "mullvad-api.h"; path = "../../mullvad-api/include/mullvad-api.h"; sourceTree = ""; }; 85C7A2E82B89024B00035D5A /* SettingsLoggedOutTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsLoggedOutTests.swift; sourceTree = ""; }; 85D039972BA4711800940E7F /* SettingsMigrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsMigrationTests.swift; sourceTree = ""; }; 85E3BDE42B70E18C00FA71FD /* Networking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = ""; }; @@ -2776,7 +2848,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 01EF6F342B6A590700125696 /* libmullvad_api.a in Frameworks */, + 0107F41E2F5B02DC0012451B /* Operations.framework in Frameworks */, + 0107F4142F5B02D70012451B /* MullvadLogging.framework in Frameworks */, + 0107F41B2F5B02DA0012451B /* MullvadSettings.framework in Frameworks */, + 0107F40D2F5B02840012451B /* MullvadTypes.framework in Frameworks */, + 0107F4172F5B02D80012451B /* MullvadREST.framework in Frameworks */, + 0107F40B2F5B02580012451B /* WireGuardKitTypes in Frameworks */, + 0107F4192F5B02D90012451B /* MullvadRustRuntime.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3594,7 +3672,6 @@ children = ( A9C75BC12C2D8C9E00B4CDF5 /* libmullvad_ios.a */, A944F2712B8E02E800473F4C /* libmullvad_ios.a */, - 01EF6F332B6A590700125696 /* libmullvad_api.a */, 584023282A407F5F007B27AC /* libmullvad_ios.a */, 01F1FF1D29F0627D007083C3 /* libmullvad_ios.a */, ); @@ -3957,7 +4034,6 @@ 58F3C0A824A50C0E003E76BE /* Assets */, 58ECD29023F178FD004298B6 /* Configurations */, 584F991F2902CBDD001F858D /* Frameworks */, - 01EF6F2D2B6A51B100125696 /* mullvad-api.h */, 8556EB512B9A1C6900D26DD4 /* MullvadApi.swift */, 58D223F4294C8FF00029F5F8 /* MullvadLogging */, F0ACE3092BE4E478006D5333 /* MullvadMockData */, @@ -4553,12 +4629,10 @@ 7A9F29342CAA8823005F2089 /* AccessMethodsTests.swift */, 852969272B4D9C1F007EAD4C /* AccountTests.swift */, 8518F6392B601910009EB113 /* Base */, - 8556EB532B9A1D7100D26DD4 /* BridgingHeader.h */, 85557B112B594FC900795FE1 /* ConnectivityTests.swift */, A9BFAFFE2BD004ED00F2BCA1 /* CustomListsTests.swift */, 852969372B4ED20E007EAD4C /* Info.plist */, 85F1E17D2C0A256200DB8F55 /* LeakTests.swift */, - 85B267602B849ADB0098E3CD /* mullvad-api.h */, 85557B0C2B591B0F00795FE1 /* Networking */, 852969312B4E9220007EAD4C /* Pages */, 7A9519932EBDDE5B00AA8B19 /* Payment */, @@ -5021,16 +5095,6 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 01EF6F2C2B6A517900125696 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - 85B267612B849ADB0098E3CD /* mullvad-api.h in Headers */, - 8556EB542B9A1D7100D26DD4 /* BridgingHeader.h in Headers */, - A9DF789B2B7D1DF10094E4AD /* mullvad-api.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 06799AB728F98E1D00ACD94E /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -5460,19 +5524,26 @@ isa = PBXNativeTarget; buildConfigurationList = 8529692F2B4D9C1F007EAD4C /* Build configuration list for PBXNativeTarget "MullvadVPNUITests" */; buildPhases = ( - 01EF6F2B2B6A512C00125696 /* ShellScript */, - 01EF6F2C2B6A517900125696 /* Headers */, 852969212B4D9C1F007EAD4C /* Sources */, 852969222B4D9C1F007EAD4C /* Frameworks */, 852969232B4D9C1F007EAD4C /* Resources */, + 0107F4162F5B02D70012451B /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( 8529692C2B4D9C1F007EAD4C /* PBXTargetDependency */, + 856E0AD32B4D9C1F007EAD4C /* PBXTargetDependency */, + 0107F3FD2F5AE1E40012451B /* PBXTargetDependency */, + 0107F4012F5AE2860012451B /* PBXTargetDependency */, + 0107F4052F5B02310012451B /* PBXTargetDependency */, + 0107F4092F5B024D0012451B /* PBXTargetDependency */, + 0107F40F2F5B02840012451B /* PBXTargetDependency */, + 0107F4132F5B02AD0012451B /* PBXTargetDependency */, ); name = MullvadVPNUITests; packageProductDependencies = ( + 0107F40A2F5B02580012451B /* WireGuardKitTypes */, ); productName = MullvadVPNUITests; productReference = 852969252B4D9C1F007EAD4C /* MullvadVPNUITests.xctest */; @@ -5841,24 +5912,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 01EF6F2B2B6A512C00125696 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nCARGO_TARGET_DIR=${PROJECT_DIR}/../target bash ${PROJECT_DIR}/build-rust-library.sh mullvad-api\n"; - }; 580E3F212A9860F20061809D /* Run SwiftLint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -7123,6 +7176,36 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 0107F3FD2F5AE1E40012451B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 06799ABB28F98E1D00ACD94E /* MullvadREST */; + targetProxy = 0107F3FC2F5AE1E40012451B /* PBXContainerItemProxy */; + }; + 0107F4012F5AE2860012451B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58B2FDD22AA71D2A003EB5C6 /* MullvadSettings */; + targetProxy = 0107F4002F5AE2860012451B /* PBXContainerItemProxy */; + }; + 0107F4052F5B02310012451B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */; + targetProxy = 0107F4042F5B02310012451B /* PBXContainerItemProxy */; + }; + 0107F4092F5B024D0012451B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58D223A4294C8A480029F5F8 /* Operations */; + targetProxy = 0107F4082F5B024D0012451B /* PBXContainerItemProxy */; + }; + 0107F40F2F5B02840012451B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */; + targetProxy = 0107F40E2F5B02840012451B /* PBXContainerItemProxy */; + }; + 0107F4132F5B02AD0012451B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 58D223F2294C8FF00029F5F8 /* MullvadLogging */; + targetProxy = 0107F4122F5B02AD0012451B /* PBXContainerItemProxy */; + }; 0107F4302F5F62950012451B /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 58B2FDD22AA71D2A003EB5C6 /* MullvadSettings */; @@ -7343,6 +7426,11 @@ target = 58CE5E5F224146200008646E /* MullvadVPN */; targetProxy = 8529692B2B4D9C1F007EAD4C /* PBXContainerItemProxy */; }; + 856E0AD32B4D9C1F007EAD4C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A992DA1C2C24709F00DE7CE5 /* MullvadRustRuntime */; + targetProxy = 856E0AD22B4D9C1F007EAD4C /* PBXContainerItemProxy */; + }; A9173C342C36CCFB00F6A08C /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 58D223D4294C8E5E0029F5F8 /* MullvadTypes */; @@ -8445,11 +8533,8 @@ "$(inherited)", ); GENERATE_INFOPLIST_FILE = YES; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/mullvad-api/include"; INFOPLIST_FILE = MullvadVPNUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 17.0; - "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug"; - "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug"; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; @@ -8460,7 +8545,6 @@ SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG MULLVAD_ENVIRONMENT_PRODUCTION $(inherited)"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = MullvadVPN; @@ -8479,11 +8563,8 @@ ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/mullvad-api/include"; INFOPLIST_FILE = MullvadVPNUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 17.0; - "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release"; - "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release"; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; @@ -8493,7 +8574,6 @@ SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = MullvadVPN; @@ -9026,11 +9106,8 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = CKG9MXH72F; GENERATE_INFOPLIST_FILE = YES; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../mullvad-api/include"; INFOPLIST_FILE = MullvadVPNUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 17.0; - "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/debug"; - "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/debug"; PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; PRODUCT_NAME = MullvadVPNUITests; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -9038,7 +9115,6 @@ SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG MULLVAD_ENVIRONMENT_STAGING"; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = MullvadVPN; @@ -9052,18 +9128,14 @@ APPLICATION_IDENTIFIER = net.mullvad.MullvadVPN; GCC_PREPROCESSOR_DEFINITIONS = ""; GENERATE_INFOPLIST_FILE = YES; - HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../mullvad-api/include"; INFOPLIST_FILE = MullvadVPNUITests/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 17.0; - "LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios/release"; - "LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/../target/aarch64-apple-ios-sim/release"; PRODUCT_BUNDLE_IDENTIFIER = "$(APPLICATION_IDENTIFIER).MullvadVPNUITests"; PRODUCT_NAME = MullvadVPNUITests; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SWIFT_ACTIVE_COMPILATION_CONDITIONS = MULLVAD_ENVIRONMENT_PRODUCTION; SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OBJC_BRIDGING_HEADER = "$(PROJECT_DIR)/MullvadVPNUITests/BridgingHeader.h"; SWIFT_VERSION = 6.0; TARGETED_DEVICE_FAMILY = "1,2"; TEST_TARGET_NAME = MullvadVPN; @@ -10285,6 +10357,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 0107F40A2F5B02580012451B /* WireGuardKitTypes */ = { + isa = XCSwiftPackageProductDependency; + package = 44A2623A2D5CF6C900085380 /* XCLocalSwiftPackageReference "wireguard-apple" */; + productName = WireGuardKitTypes; + }; 44A2623D2D5CF9FD00085380 /* WireGuardKitTypes */ = { isa = XCSwiftPackageProductDependency; package = 44A2623A2D5CF6C900085380 /* XCLocalSwiftPackageReference "wireguard-apple" */; diff --git a/ios/MullvadVPNUITests/BridgingHeader.h b/ios/MullvadVPNUITests/BridgingHeader.h index 3778a8141b2e..7ff77b3810a6 100644 --- a/ios/MullvadVPNUITests/BridgingHeader.h +++ b/ios/MullvadVPNUITests/BridgingHeader.h @@ -7,4 +7,3 @@ // #import -#include "mullvad-api.h" diff --git a/ios/MullvadVPNUITests/MullvadApi.swift b/ios/MullvadVPNUITests/MullvadApi.swift index a3f6e0a5da54..9eb0c643e347 100644 --- a/ios/MullvadVPNUITests/MullvadApi.swift +++ b/ios/MullvadVPNUITests/MullvadApi.swift @@ -7,148 +7,147 @@ // import Foundation +import MullvadLogging +import MullvadRustRuntime -struct ApiError: Error { +struct MullvadApiError: Error { let description: String - let kind: MullvadApiErrorKind - init(_ result: MullvadApiError) { - kind = result.kind - if result.description != nil { - description = String(cString: result.description) - } else { - description = "No error" - } - mullvad_api_error_drop(result) - } - - func throwIfErr() throws { - if self.kind.rawValue != 0 { - throw self - } - } -} - -struct InitMutableBufferError: Error { - let description = "Failed to allocate memory for mutable buffer" } struct Device { let name: String let id: UUID +} - init(device_struct: MullvadApiDevice) { - name = String(cString: device_struct.name_ptr) - id = UUID(uuid: device_struct.id) - } +private struct NewAccountResponse: Decodable { let number: String } +private struct AccountResponse: Decodable { let expiry: Date } +private struct DeviceResponse: Decodable { + let id: String + let name: String } -/// - Warning: Do not change the `apiAddress` or the `hostname` after the time `MullvadApi.init` has been invoked -/// The Mullvad API crate is using a global static variable to store those. They will be initialized only once. -/// +/// - Warning: Do not change the `apiAddress` or the `hostname` after the time `MullvadApi.init` has been invoked. class MullvadApi { - private var clientContext = MullvadApiClient() + private let context: SwiftApiContext + + private static let logger = Logger(label: "MullvadApi") - /// Initialize the Mullvad API client - /// - Parameters: - /// - apiAddress: Address of the Mullvad API server in the format \:\ - /// - hostname: Hostname of the Mullvad API server init(apiAddress: String, hostname: String) throws { - let result = mullvad_api_client_initialize( - &clientContext, + Self.logger.debug("Initializing MullvadApi with address: \(apiAddress), hostname: \(hostname)") + let directRaw = convert_builtin_access_method_setting( + UUID().uuidString, "Direct", true, UInt8(KindDirect.rawValue), nil + ) + // Bridges and EncryptedDNS must be disabled because the shadowsocks bridge provider + // is initialized with a nil loader. If Direct fails and the access method selector + // falls back to Bridges, it will dereference the nil pointer and SIGABRT. + let bridgesRaw = convert_builtin_access_method_setting( + UUID().uuidString, "Bridges", false, UInt8(KindBridge.rawValue), nil + ) + let encryptedDNSRaw = convert_builtin_access_method_setting( + UUID().uuidString, + "EncryptedDNS", + false, + UInt8( + KindEncryptedDnsProxy.rawValue + ), + nil + ) + let settingsWrapper = init_access_method_settings_wrapper( + directRaw, bridgesRaw, encryptedDNSRaw, nil, 0 + ) + let bridgeProvider = SwiftShadowsocksLoaderWrapper( + _0: SwiftShadowsocksLoaderWrapperContext(shadowsocks_loader: nil) + ) + context = mullvad_api_init_inner( + hostname, apiAddress, hostname, - false + false, + bridgeProvider, + settingsWrapper, + nil, + nil ) - try ApiError(result).throwIfErr() } - /// Removes all devices assigned to the specified account - func removeAllDevices(forAccount: String) throws { - let result = mullvad_api_remove_all_devices( - clientContext, - forAccount - ) + func createAccount() throws -> String { + let response = try makeRequest { cookie, strategy in + mullvad_ios_create_account(context, cookie, strategy) + } + let data = try requireBody(response) + return try JSONDecoder().decode(NewAccountResponse.self, from: data).number + } - try ApiError(result).throwIfErr() + func delete(account: String) throws { + _ = try makeRequest { cookie, strategy in + mullvad_ios_delete_account(context, cookie, strategy, account) + } } - /// Public key must be at least 32 bytes long - only 32 bytes of it will be read func addDevice(forAccount: String, publicKey: Data) throws { - var device = MullvadApiDevice() - let result = mullvad_api_add_device( - clientContext, - forAccount, - (publicKey as NSData).bytes, - &device - ) - - try ApiError(result).throwIfErr() + _ = try publicKey.withUnsafeBytes { ptr -> MullvadApiResponse in + try makeRequest { cookie, strategy in + mullvad_ios_create_device( + context, + cookie, + strategy, + forAccount, + ptr.baseAddress!.assumingMemoryBound(to: UInt8.self) + ) + } + } } - /// Returns a unix timestamp of the expiry date for the specified account. func getExpiry(forAccount: String) throws -> UInt64 { - var expiry = UInt64(0) - let result = mullvad_api_get_expiry(clientContext, forAccount, &expiry) - - try ApiError(result).throwIfErr() - - return expiry - } - - func createAccount() throws -> String { - var newAccountPtr: UnsafePointer? - let result = mullvad_api_create_account( - clientContext, - &newAccountPtr - ) - try ApiError(result).throwIfErr() - - let newAccount = String(cString: newAccountPtr!) - return newAccount + let response = try makeRequest { cookie, strategy in + mullvad_ios_get_account(context, cookie, strategy, forAccount) + } + let data = try requireBody(response) + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + let decoded = try decoder.decode(AccountResponse.self, from: data) + return UInt64(decoded.expiry.timeIntervalSince1970) } func listDevices(forAccount: String) throws -> [Device] { - var iterator = MullvadApiDeviceIterator() - let result = mullvad_api_list_devices(clientContext, forAccount, &iterator) - try ApiError(result).throwIfErr() - - return DeviceIterator(iter: iterator).collect() - } - - func delete(account: String) throws { - let result = mullvad_api_delete_account(clientContext, account) - try ApiError(result).throwIfErr() + let response = try makeRequest { cookie, strategy in + mullvad_ios_get_devices(context, cookie, strategy, forAccount) + } + let data = try requireBody(response) + let deviceResponses = try JSONDecoder().decode([DeviceResponse].self, from: data) + return deviceResponses.compactMap { d in + guard let uuid = UUID(uuidString: d.id) else { return nil } + return Device(name: d.name, id: uuid) + } } - deinit { - mullvad_api_client_drop(clientContext) + private func requireBody(_ response: MullvadApiResponse) throws -> Data { + guard response.success, let data = response.body else { + throw MullvadApiError(description: response.errorDescription ?? "Request failed") + } + return data } - class DeviceIterator { - private let backingIter: MullvadApiDeviceIterator - - init(iter: MullvadApiDeviceIterator) { - backingIter = iter - } + @discardableResult + private func makeRequest( + _ call: (UnsafeMutableRawPointer, SwiftRetryStrategy) -> SwiftCancelHandle + ) throws -> MullvadApiResponse { + let semaphore = DispatchSemaphore(value: 0) + nonisolated(unsafe) var apiResponse: MullvadApiResponse? - func collect() -> [Device] { - var nextDevice = MullvadApiDevice() - var devices: [Device] = [] - while mullvad_api_device_iter_next(backingIter, &nextDevice) { - devices.append(Device(device_struct: nextDevice)) - mullvad_api_device_drop(nextDevice) - } - return devices + let completion = MullvadApiCompletion { response in + apiResponse = response + semaphore.signal() } - - deinit { - mullvad_api_device_iter_drop(backingIter) + let cookie = Unmanaged.passRetained(completion).toOpaque() + let strategy = mullvad_api_retry_strategy_constant(3, 1) + var handle = call(cookie, strategy) + semaphore.wait() + mullvad_api_cancel_task_drop(&handle) + + guard let response = apiResponse else { + throw MullvadApiError(description: "No response received") } - } -} - -private extension String { - func lengthOfBytes() -> UInt { - return UInt(self.lengthOfBytes(using: String.Encoding.utf8)) + return response } } diff --git a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift index aa7e69d66108..6566743ac3da 100644 --- a/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift +++ b/ios/MullvadVPNUITests/Networking/MullvadAPIWrapper.swift @@ -8,6 +8,7 @@ import CryptoKit import Foundation +import MullvadRustRuntime import XCTest enum MullvadAPIError: Error { @@ -32,6 +33,7 @@ class MullvadAPIWrapper: @unchecked Sendable { .infoDictionary?["ApiEndpoint"] as! String init() throws { + RustLogging.initialize() let apiAddress = try Self.getAPIIPAddress() + ":" + Self.getAPIPort() let hostname = Self.hostName mullvadAPI = try MullvadApi(apiAddress: apiAddress, hostname: hostname) diff --git a/mullvad-api/Cargo.toml b/mullvad-api/Cargo.toml index d5745521ff83..5854d2bf317b 100644 --- a/mullvad-api/Cargo.toml +++ b/mullvad-api/Cargo.toml @@ -52,18 +52,13 @@ tokio-rustls = { version = "0.26.0", default-features = false, features = [ tokio-socks = "0.5.1" tower = { workspace = true } tracing-subscriber = { workspace = true, optional = true } -uuid = { version = "1.4.1", features = ["v4"] } vec1 = { workspace = true, features = ["serde"] } webpki-roots = { workspace = true, optional = true } [dev-dependencies] -mockito = "1.6.1" talpid-time = { path = "../talpid-time", features = ["test"] } tokio = { workspace = true, features = ["test-util", "time"] } -[build-dependencies] -cbindgen = { version = "0.28.0", default-features = false } - [target.'cfg(any(target_os = "windows", target_os = "macos", target_os = "linux"))'.dependencies] mullvad-update = { path = "../mullvad-update", features = ["client"] } diff --git a/mullvad-api/build.rs b/mullvad-api/build.rs deleted file mode 100644 index ecb1080a0d00..000000000000 --- a/mullvad-api/build.rs +++ /dev/null @@ -1,15 +0,0 @@ -fn main() { - let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - - match std::env::var("TARGET").unwrap().as_str() { - "aarch64-apple-ios" | "aarch64-apple-ios-sim" => { - cbindgen::Builder::new() - .with_crate(crate_dir) - .with_language(cbindgen::Language::C) - .generate() - .expect("failed to generate bindings") - .write_to_file("include/mullvad-api.h"); - } - _ => (), - } -} diff --git a/mullvad-api/include/mullvad-api.h b/mullvad-api/include/mullvad-api.h deleted file mode 100644 index 4e5f78aef28b..000000000000 --- a/mullvad-api/include/mullvad-api.h +++ /dev/null @@ -1,175 +0,0 @@ -#include -#include -#include -#include - -typedef enum MullvadApiErrorKind { - NoError = 0, - StringParsing = -1, - SocketAddressParsing = -2, - AsyncRuntimeInitialization = -3, - BadResponse = -4, -} MullvadApiErrorKind; - -typedef struct DeviceIterator DeviceIterator; - -/** - * A Mullvad API client that can be used via a C FFI. - */ -typedef struct FfiClient FfiClient; - -/** - * MullvadApiErrorKind contains a description and an error kind. If the error kind is - * `MullvadApiErrorKind` is NoError, the pointer will be nil. - */ -typedef struct MullvadApiError { - char *description; - enum MullvadApiErrorKind kind; -} MullvadApiError; - -typedef struct MullvadApiClient { - const struct FfiClient *ptr; -} MullvadApiClient; - -typedef struct MullvadApiDeviceIterator { - struct DeviceIterator *ptr; -} MullvadApiDeviceIterator; - -typedef struct MullvadApiDevice { - const char *name_ptr; - uint8_t id[16]; -} MullvadApiDevice; - -/** - * Initializes a Mullvad API client. - * - * # Safety - * - * * `client_ptr`: Must be a pointer to that is valid for the length of a `MullvadApiClient` - * struct. - * - * * `api_address`: pointer to nul-terminated UTF-8 string containing a socket address - * representation ("143.32.4.32:9090"), the port is mandatory. - * - * * `hostname`: pointer to a null-terminated UTF-8 string representing the hostname that will be - * used for TLS validation. - * * `disable_tls`: only valid when built for tests, can be ignored when consumed by Swift. - */ -struct MullvadApiError mullvad_api_client_initialize(struct MullvadApiClient *client_ptr, - const char *api_address_ptr, - const char *hostname, - bool disable_tls); - -/** - * Removes all devices from a given account - * - * # Safety - * - * * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` - * - * * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the - * account that will have all of it's devices removed. - */ -struct MullvadApiError mullvad_api_remove_all_devices(struct MullvadApiClient client_ptr, - const char *account_ptr); - -/** - * Removes all devices from a given account - * - * # Safety - * * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` - * - * * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the - * account that will have all of it's devices removed. - * - * * `expiry_unix_timestamp`: a pointer to a signed 64 bit integer. If this function returns no - * error, the expiry timestamp will be written to this pointer. - */ -struct MullvadApiError mullvad_api_get_expiry(struct MullvadApiClient client_ptr, - const char *account_str_ptr, - int64_t *expiry_unix_timestamp); - -/** - * Gets a list of all devices associated with the specified account from the API. - * - * # Safety - * - * * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` - * - * * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the - * account that will have all of it's devices removed. - * - * * `device_iter_ptr`: a pointer to a `device::MullvadApiDeviceIterator`. If this function doesn't - * return an error, the pointer will be initialized with a valid instance of - * `device::MullvadApiDeviceIterator`, which can be used to iterate through the devices. - */ -struct MullvadApiError mullvad_api_list_devices(struct MullvadApiClient client_ptr, - const char *account_str_ptr, - struct MullvadApiDeviceIterator *device_iter_ptr); - -/** - * Adds a device to the specified account with the specified public key. Note that the device - * name, associated addresess and UUID are not returned. - * - * # Safety - * - * * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` - * - * * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the - * account that will have a device added to ita device added to it. - * - * * `public_key_ptr`: a pointer to 32 bytes of a WireGuard public key that will be uploaded. - * - * * `new_device_ptr`: a pointer to enough memory to allocate a `MullvadApiDevice`. If this - * function doesn't return an error, it will be initialized. - */ -struct MullvadApiError mullvad_api_add_device(struct MullvadApiClient client_ptr, - const char *account_str_ptr, - const uint8_t *public_key_ptr, - struct MullvadApiDevice *new_device_ptr); - -/** - * Creates a new account. - * - * # Safety - * - * * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` - * - * * `account_str_ptr`: If a new account is created successfully, a pointer to an allocated C - * string containing the new account number will be written to this pointer. It must be freed via - * `mullvad_api_cstring_drop`. - */ -struct MullvadApiError mullvad_api_create_account(struct MullvadApiClient client_ptr, - const char **account_str_ptr); - -/** - * Deletes the specified account. - * - * # Safety - * - * * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` - * - * * `account_str_ptr`: Must be a null-terminated string representing the account to be deleted. - */ -struct MullvadApiError mullvad_api_delete_account(struct MullvadApiClient client_ptr, - const char *account_str_ptr); - -void mullvad_api_client_drop(struct MullvadApiClient client); - -/** - * Deallocates a CString returned by the Mullvad API client. - * - * # Safety - * - * `cstr_ptr` must be a pointer to a string allocated by another `mullvad_api` function. - */ -void mullvad_api_cstring_drop(char *cstr_ptr); - -bool mullvad_api_device_iter_next(struct MullvadApiDeviceIterator iter, - struct MullvadApiDevice *device_ptr); - -void mullvad_api_device_iter_drop(struct MullvadApiDeviceIterator iter); - -void mullvad_api_device_drop(struct MullvadApiDevice device); - -void mullvad_api_error_drop(struct MullvadApiError error); diff --git a/mullvad-api/src/ffi/device.rs b/mullvad-api/src/ffi/device.rs deleted file mode 100644 index 9007d6d8dfb1..000000000000 --- a/mullvad-api/src/ffi/device.rs +++ /dev/null @@ -1,104 +0,0 @@ -use mullvad_types::device::Device; -use std::{ffi::CString, ptr}; - -#[repr(C)] -pub struct MullvadApiDeviceIterator { - ptr: *mut DeviceIterator, -} - -impl MullvadApiDeviceIterator { - pub fn new(iter: impl IntoIterator + 'static) -> Self { - let iter = Box::new(DeviceIterator::from(iter)); - - Self { - ptr: Box::into_raw(iter), - } - } - - fn is_done(&self) -> bool { - self.ptr.is_null() - } - - unsafe fn as_iter(&mut self) -> &mut Box> { - let wrapper = unsafe { &mut *self.ptr }; - &mut wrapper.iter - } - - fn drop(mut self) { - if self.ptr.is_null() { - return; - } - - let _ = unsafe { Box::from_raw(self.ptr) }; - self.ptr = ptr::null_mut(); - } -} - -#[repr(C)] -pub struct MullvadApiDevice { - name_ptr: *const libc::c_char, - id: [u8; 16], -} - -impl From for MullvadApiDevice { - fn from(dev: Device) -> Self { - let name = CString::new(dev.name).expect("Null bytes in name from API response"); - let name_ptr = name.into_raw(); - let id = *uuid::Uuid::parse_str(&dev.id) - .expect("Failed to parse UUID") - .as_bytes(); - - Self { name_ptr, id } - } -} - -impl MullvadApiDevice { - fn drop(self) { - let _ = unsafe { CString::from_raw(self.name_ptr as *mut _) }; - } -} - -struct DeviceIterator { - iter: Box>, -} - -impl From for DeviceIterator -where - T: IntoIterator + 'static, -{ - fn from(i: T) -> Self { - let iter: Box> = Box::new(i.into_iter()); - Self { iter } - } -} - -#[unsafe(no_mangle)] -pub extern "C" fn mullvad_api_device_iter_next( - mut iter: MullvadApiDeviceIterator, - device_ptr: *mut MullvadApiDevice, -) -> bool { - if iter.is_done() { - return false; - } - - // SAFETY: Asuming self.ptr is still valid since iter.is_done() returned false; - let iter = unsafe { iter.as_iter() }; - let Some(device) = iter.next() else { - return false; - }; - - // SAFETY: Assuming device pointer is valid - unsafe { ptr::write(device_ptr, device.into()) } - - true -} - -#[unsafe(no_mangle)] -pub extern "C" fn mullvad_api_device_iter_drop(iter: MullvadApiDeviceIterator) { - iter.drop() -} - -#[unsafe(no_mangle)] -pub extern "C" fn mullvad_api_device_drop(device: MullvadApiDevice) { - device.drop() -} diff --git a/mullvad-api/src/ffi/error.rs b/mullvad-api/src/ffi/error.rs deleted file mode 100644 index 2468515c25e5..000000000000 --- a/mullvad-api/src/ffi/error.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::rest; -use std::ffi::CString; - -#[derive(Debug, PartialEq)] -#[repr(C)] -pub enum MullvadApiErrorKind { - NoError = 0, - StringParsing = -1, - SocketAddressParsing = -2, - AsyncRuntimeInitialization = -3, - BadResponse = -4, -} - -/// MullvadApiErrorKind contains a description and an error kind. If the error kind is -/// `MullvadApiErrorKind` is NoError, the pointer will be nil. -#[derive(Debug)] -#[repr(C)] -pub struct MullvadApiError { - description: *mut libc::c_char, - kind: MullvadApiErrorKind, -} - -impl MullvadApiError { - pub fn new(kind: MullvadApiErrorKind, error: &dyn std::error::Error) -> Self { - let description = CString::new(format!("{error:?}: {error}")).unwrap_or_default(); - Self { - description: description.into_raw(), - kind, - } - } - - pub fn api_err(error: rest::Error) -> Self { - Self::new(MullvadApiErrorKind::BadResponse, &error) - } - - pub fn with_str(kind: MullvadApiErrorKind, description: &str) -> Self { - let description = CString::new(description).unwrap_or_default(); - Self { - description: description.into_raw(), - kind, - } - } - - pub fn ok() -> MullvadApiError { - Self { - description: std::ptr::null_mut(), - kind: MullvadApiErrorKind::NoError, - } - } - - pub fn unwrap(&self) { - if !matches!(self.kind, MullvadApiErrorKind::NoError) { - let desc = unsafe { std::ffi::CStr::from_ptr(self.description) }; - panic!("API ERROR - {:?} - {}", self.kind, desc.to_str().unwrap()); - } - } - - pub fn drop(self) { - if self.description.is_null() { - return; - } - - let _ = unsafe { CString::from_raw(self.description) }; - } -} - -#[unsafe(no_mangle)] -pub extern "C" fn mullvad_api_error_drop(error: MullvadApiError) { - error.drop() -} diff --git a/mullvad-api/src/ffi/mod.rs b/mullvad-api/src/ffi/mod.rs deleted file mode 100644 index 1091d3bbd4f3..000000000000 --- a/mullvad-api/src/ffi/mod.rs +++ /dev/null @@ -1,517 +0,0 @@ -#![cfg(not(target_os = "android"))] -#![allow(clippy::undocumented_unsafe_blocks)] // Remove me if you dare. - -use std::{ - ffi::{CStr, CString}, - net::SocketAddr, - ptr, - sync::Arc, -}; - -use crate::{ - AccountsProxy, ApiEndpoint, DevicesProxy, - proxy::ApiConnectionMode, - rest::{self, MullvadRestHandle}, -}; - -mod device; -mod error; - -pub use error::{MullvadApiError, MullvadApiErrorKind}; - -#[repr(C)] -pub struct MullvadApiClient { - ptr: *const FfiClient, -} - -impl MullvadApiClient { - fn new(client: FfiClient) -> Self { - let arc = Arc::new(client); - let ptr = Arc::into_raw(arc); - Self { ptr } - } - - unsafe fn get_client(&self) -> Arc { - // Incrementing before creating an Arc from a pointer. This way multiple threads can use - // it, and a single thread can decrement it. - unsafe { Arc::increment_strong_count(self.ptr) }; - - unsafe { Arc::from_raw(self.ptr) } - } - - fn drop(self) { - if self.ptr.is_null() { - return; - } - - let _ = unsafe { Arc::from_raw(self.ptr) }; - } -} - -/// A Mullvad API client that can be used via a C FFI. -struct FfiClient { - tokio_runtime: tokio::runtime::Runtime, - api_runtime: crate::Runtime, -} - -impl FfiClient { - unsafe fn new( - api_address_ptr: *const libc::c_char, - hostname: *const libc::c_char, - #[cfg(any(feature = "api-override", test))] disable_tls: bool, - ) -> Result { - // SAFETY: addr_str must be a valid pointer to a null-terminated string. - let addr_str = unsafe { string_from_raw_ptr(api_address_ptr)? }; - // SAFETY: api_hostname must be a valid pointer to a null-terminated string. - let api_hostname = unsafe { string_from_raw_ptr(hostname)? }; - - let api_address: SocketAddr = addr_str.parse().map_err(|_| { - MullvadApiError::with_str( - MullvadApiErrorKind::SocketAddressParsing, - "Failed to parse API socket address", - ) - })?; - - let endpoint = ApiEndpoint { - host: Some(api_hostname.clone()), - address: Some(api_address), - #[cfg(feature = "api-override")] - force_direct: false, - #[cfg(any(feature = "api-override", test))] - disable_tls, - }; - - let mut runtime_builder = tokio::runtime::Builder::new_multi_thread(); - - runtime_builder.worker_threads(2).enable_all(); - let tokio_runtime = runtime_builder.build().map_err(|err| { - MullvadApiError::new(MullvadApiErrorKind::AsyncRuntimeInitialization, &err) - })?; - - // It is imperative that the REST runtime is created within an async context, otherwise - // ApiAvailability panics. - let api_runtime = tokio_runtime - .block_on(async { crate::Runtime::new(tokio_runtime.handle().clone(), &endpoint) }); - - let context = FfiClient { - tokio_runtime, - api_runtime, - }; - - Ok(context) - } - - unsafe fn add_device( - self: Arc, - account_str_ptr: *const libc::c_char, - public_key_ptr: *const u8, - ) -> Result { - // SAFETY: account_str_ptr must be a valid pointer to a null-terminated string. - let account = unsafe { string_from_raw_ptr(account_str_ptr)? }; - - // SAFETY: assuming public_key_ptr is valid for 32 bytes - let public_key_bytes: [u8; 32] = unsafe { std::ptr::read(public_key_ptr as *const _) }; - let public_key = public_key_bytes.into(); - - let runtime = self.tokio_handle(); - - let device_proxy = self.device_proxy(); - - let device = runtime - .block_on(async move { - let (device, _) = device_proxy.create(account, public_key).await?; - Ok(device) - }) - .map_err(MullvadApiError::api_err)?; - - Ok(device.into()) - } - - unsafe fn create_account(self: Arc) -> Result { - let accounts_proxy = self.accounts_proxy(); - - self.tokio_handle() - .block_on(async move { - let new_account = accounts_proxy.create_account().await?; - Ok(new_account) - }) - .map_err(MullvadApiError::api_err) - } - - unsafe fn get_expiry( - self: Arc, - account_str_ptr: *const libc::c_char, - ) -> Result { - // SAFETY: account_str_ptr must be a valid pointer to a null-terminated string. - let account = unsafe { string_from_raw_ptr(account_str_ptr)? }; - - let account_proxy = self.accounts_proxy(); - self.tokio_handle() - .block_on(async move { - let expiry_timestamp = account_proxy.get_data(account).await?.expiry.timestamp(); - Ok(expiry_timestamp) - }) - .map_err(MullvadApiError::api_err) - } - - unsafe fn remove_all_devices( - self: Arc, - account_str_ptr: *const libc::c_char, - ) -> Result<(), MullvadApiError> { - // SAFETY: account_str_ptr must be a valid pointer to a null-terminated string. - let account = unsafe { string_from_raw_ptr(account_str_ptr)? }; - - let runtime = self.tokio_handle(); - let device_proxy = self.device_proxy(); - runtime - .block_on(async move { - let devices = device_proxy.list(account.clone()).await?; - for device in devices { - device_proxy.remove(account.clone(), device.id).await?; - } - Result::<_, rest::Error>::Ok(()) - }) - .map_err(MullvadApiError::api_err) - } - - unsafe fn list_devices( - self: Arc, - account_str_ptr: *const libc::c_char, - ) -> Result { - // SAFETY: account_str_ptr must be a valid pointer to a null-terminated string. - let account = unsafe { string_from_raw_ptr(account_str_ptr)? }; - - let runtime = self.tokio_handle(); - let device_proxy = self.device_proxy(); - - let devices = runtime - .block_on(device_proxy.list(account)) - .map_err(MullvadApiError::api_err)?; - - Ok(device::MullvadApiDeviceIterator::new(devices)) - } - - unsafe fn delete_account( - self: Arc, - account_str_ptr: *const libc::c_char, - ) -> Result<(), MullvadApiError> { - // SAFETY: account_str_ptr must be a valid pointer to a null-terminated string. - let account = unsafe { string_from_raw_ptr(account_str_ptr)? }; - - let runtime = self.tokio_handle(); - let accounts_proxy = self.accounts_proxy(); - - runtime - .block_on(accounts_proxy.delete_account(account)) - .map_err(MullvadApiError::api_err) - } - - fn rest_handle(&self) -> MullvadRestHandle { - self.tokio_handle().block_on(async { - self.api_runtime - .mullvad_rest_handle(ApiConnectionMode::Direct.into_provider()) - }) - } - - fn device_proxy(&self) -> DevicesProxy { - crate::DevicesProxy::new(self.rest_handle()) - } - - fn accounts_proxy(&self) -> AccountsProxy { - crate::AccountsProxy::new(self.rest_handle()) - } - - fn tokio_handle(&self) -> tokio::runtime::Handle { - self.tokio_runtime.handle().clone() - } -} - -/// Initializes a Mullvad API client. -/// -/// # Safety -/// -/// * `client_ptr`: Must be a pointer to that is valid for the length of a `MullvadApiClient` -/// struct. -/// -/// * `api_address`: pointer to nul-terminated UTF-8 string containing a socket address -/// representation ("143.32.4.32:9090"), the port is mandatory. -/// -/// * `hostname`: pointer to a null-terminated UTF-8 string representing the hostname that will be -/// used for TLS validation. -/// * `disable_tls`: only valid when built for tests, can be ignored when consumed by Swift. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_client_initialize( - client_ptr: *mut MullvadApiClient, - api_address_ptr: *const libc::c_char, - hostname: *const libc::c_char, - disable_tls: bool, -) -> MullvadApiError { - #[cfg(not(any(feature = "api-override", test)))] - if disable_tls { - log::error!("disable_tls has no effect when mullvad-api is built without api-override"); - } - - match unsafe { - FfiClient::new( - api_address_ptr, - hostname, - #[cfg(any(feature = "api-override", test))] - disable_tls, - ) - } { - Ok(client) => { - unsafe { - std::ptr::write(client_ptr, MullvadApiClient::new(client)); - }; - MullvadApiError::ok() - } - Err(err) => err, - } -} - -/// Removes all devices from a given account -/// -/// # Safety -/// -/// * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` -/// -/// * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the -/// account that will have all of it's devices removed. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_remove_all_devices( - client_ptr: MullvadApiClient, - account_ptr: *const libc::c_char, -) -> MullvadApiError { - let client = unsafe { client_ptr.get_client() }; - match unsafe { client.remove_all_devices(account_ptr) } { - Ok(_) => MullvadApiError::ok(), - Err(err) => err, - } -} - -/// Removes all devices from a given account -/// -/// # Safety -/// * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` -/// -/// * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the -/// account that will have all of it's devices removed. -/// -/// * `expiry_unix_timestamp`: a pointer to a signed 64 bit integer. If this function returns no -/// error, the expiry timestamp will be written to this pointer. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_get_expiry( - client_ptr: MullvadApiClient, - account_str_ptr: *const libc::c_char, - expiry_unix_timestamp: *mut i64, -) -> MullvadApiError { - let client = unsafe { client_ptr.get_client() }; - match unsafe { client.get_expiry(account_str_ptr) } { - Ok(expiry) => { - unsafe { ptr::write(expiry_unix_timestamp, expiry) }; - MullvadApiError::ok() - } - Err(err) => err, - } -} - -/// Gets a list of all devices associated with the specified account from the API. -/// -/// # Safety -/// -/// * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` -/// -/// * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the -/// account that will have all of it's devices removed. -/// -/// * `device_iter_ptr`: a pointer to a `device::MullvadApiDeviceIterator`. If this function doesn't -/// return an error, the pointer will be initialized with a valid instance of -/// `device::MullvadApiDeviceIterator`, which can be used to iterate through the devices. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_list_devices( - client_ptr: MullvadApiClient, - account_str_ptr: *const libc::c_char, - device_iter_ptr: *mut device::MullvadApiDeviceIterator, -) -> MullvadApiError { - let client = unsafe { client_ptr.get_client() }; - match unsafe { client.list_devices(account_str_ptr) } { - Ok(iter) => { - unsafe { ptr::write(device_iter_ptr, iter) }; - MullvadApiError::ok() - } - Err(err) => err, - } -} - -/// Adds a device to the specified account with the specified public key. Note that the device -/// name, associated addresess and UUID are not returned. -/// -/// # Safety -/// -/// * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` -/// -/// * `account_str_ptr`: pointer to nul-terminated UTF-8 string containing the account number of the -/// account that will have a device added to ita device added to it. -/// -/// * `public_key_ptr`: a pointer to 32 bytes of a WireGuard public key that will be uploaded. -/// -/// * `new_device_ptr`: a pointer to enough memory to allocate a `MullvadApiDevice`. If this -/// function doesn't return an error, it will be initialized. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_add_device( - client_ptr: MullvadApiClient, - account_str_ptr: *const libc::c_char, - public_key_ptr: *const u8, - new_device_ptr: *mut device::MullvadApiDevice, -) -> MullvadApiError { - // SAFETY: Assuming MullvadApiClient is initialized - let client = unsafe { client_ptr.get_client() }; - // SAFETY: Asuming `new_device_ptr` is valid. - match unsafe { client.add_device(account_str_ptr, public_key_ptr) } { - Ok(device) => { - // SAFETY: Asuming `new_device_ptr` is valid. - // SAFETY: Asuming `new_device_ptr` is valid. - unsafe { ptr::write(new_device_ptr, device) }; - MullvadApiError::ok() - } - Err(err) => err, - } -} - -/// Creates a new account. -/// -/// # Safety -/// -/// * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` -/// -/// * `account_str_ptr`: If a new account is created successfully, a pointer to an allocated C -/// string containing the new account number will be written to this pointer. It must be freed via -/// `mullvad_api_cstring_drop`. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_create_account( - client_ptr: MullvadApiClient, - account_str_ptr: *mut *const libc::c_char, -) -> MullvadApiError { - let client = unsafe { client_ptr.get_client() }; - match unsafe { client.create_account() } { - Ok(new_account) => { - let Ok(account) = CString::new(new_account) else { - return MullvadApiError::with_str( - MullvadApiErrorKind::BadResponse, - "Account number string c ontained null bytes", - ); - }; - - unsafe { ptr::write(account_str_ptr, account.into_raw()) }; - MullvadApiError::ok() - } - Err(err) => err, - } -} - -/// Deletes the specified account. -/// -/// # Safety -/// -/// * `client_ptr`: Must be a valid, initialized instance of `MullvadApiClient` -/// -/// * `account_str_ptr`: Must be a null-terminated string representing the account to be deleted. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_delete_account( - client_ptr: MullvadApiClient, - account_str_ptr: *const libc::c_char, -) -> MullvadApiError { - let client = unsafe { client_ptr.get_client() }; - match unsafe { client.delete_account(account_str_ptr) } { - Ok(_) => MullvadApiError::ok(), - Err(err) => err, - } -} - -#[unsafe(no_mangle)] -pub extern "C" fn mullvad_api_client_drop(client: MullvadApiClient) { - client.drop() -} - -/// Deallocates a CString returned by the Mullvad API client. -/// -/// # Safety -/// -/// `cstr_ptr` must be a pointer to a string allocated by another `mullvad_api` function. -#[unsafe(no_mangle)] -pub unsafe extern "C" fn mullvad_api_cstring_drop(cstr_ptr: *mut libc::c_char) { - let _ = unsafe { CString::from_raw(cstr_ptr) }; -} - -/// The return value is only valid for the lifetime of the `ptr` that's passed in -/// -/// # Safety -/// -/// `ptr` must be valid for `size` bytes -unsafe fn string_from_raw_ptr(ptr: *const libc::c_char) -> Result { - let cstr = unsafe { CStr::from_ptr(ptr) }; - - Ok(cstr - .to_str() - .map_err(|_| { - MullvadApiError::with_str( - MullvadApiErrorKind::StringParsing, - "Failed to parse UTF-8 string", - ) - })? - .to_owned()) -} - -#[cfg(test)] -mod test { - use mockito::{Server, ServerGuard}; - use std::{mem::MaybeUninit, net::Ipv4Addr}; - - use super::*; - const STAGING_HOSTNAME: &[u8] = b"api-app.stagemole.eu\0"; - - #[test] - fn test_initialization() { - let _ = create_client(&SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), 1)); - } - - fn create_client(addr: &SocketAddr) -> MullvadApiClient { - let mut client = MaybeUninit::::uninit(); - let cstr_address = CString::new(addr.to_string()).unwrap(); - unsafe { - mullvad_api_client_initialize( - client.as_mut_ptr(), - cstr_address.as_ptr().cast(), - STAGING_HOSTNAME.as_ptr().cast(), - true, - ) - .unwrap(); - }; - unsafe { client.assume_init() } - } - - #[test] - fn test_create_delete_account() { - let server = test_server(); - let client = create_client(&server.socket_address()); - - let mut account_buf = vec![0 as libc::c_char; 100]; - unsafe { mullvad_api_create_account(client, account_buf.as_mut_ptr().cast()).unwrap() }; - } - - fn test_server() -> ServerGuard { - let mut server = Server::new(); - let expected_create_account_response = br#"{"id":"085df870-0fc2-47cb-9e8c-cb43c1bdaac0","expiry":"2024-12-11T12:56:32+00:00","max_ports":0,"can_add_ports":false,"max_devices":5,"can_add_devices":true,"number":"6705749539195318"}"#; - server - .mock( - "POST", - &*("/".to_string() + crate::ACCOUNTS_URL_PREFIX + "/accounts"), - ) - .with_header("content-type", "application/json") - .with_status(201) - .with_body(expected_create_account_response) - .create(); - - server - } -} diff --git a/mullvad-api/src/lib.rs b/mullvad-api/src/lib.rs index 30a498aa5cad..5761d79ac5ef 100644 --- a/mullvad-api/src/lib.rs +++ b/mullvad-api/src/lib.rs @@ -31,8 +31,6 @@ pub mod device; pub mod domain_fronting; mod relay_list; -pub mod ffi; - pub use address_cache::Error as AddressCacheError; pub use address_cache::{AddressCache, AddressCacheBacking, FileAddressCacheBacking}; pub use device::DevicesProxy; diff --git a/mullvad-ios/src/api_client/access_method_settings.rs b/mullvad-ios/src/api_client/access_method_settings.rs index b6986492b536..99bff49b4ea3 100644 --- a/mullvad-ios/src/api_client/access_method_settings.rs +++ b/mullvad-ios/src/api_client/access_method_settings.rs @@ -155,12 +155,17 @@ pub unsafe extern "C" fn init_access_method_settings_wrapper( /// This takes ownership of the `AccessMethodSetting`s pointed to by `raw_array`. So the memory is /// freed when the returned vector is dropped. /// -/// SAFETY: `raw_array` must be aligned, non-null, initialized for `count` reads and not be used -/// after this call. +/// SAFETY: `raw_array` must be aligned, initialized for `count` reads and not be used after this +/// call. unsafe fn access_methods_from_raw_array( raw_array: *const *mut AccessMethodSetting, number_of_elements: usize, ) -> Vec { + // `slice::from_raw_parts` will dereference the null pointer even if the slice is empty. + // In that case, just return an empty vec. + if number_of_elements == 0 || raw_array.is_null() { + return vec![]; + } // SAFETY: See notice above let slice = unsafe { slice::from_raw_parts(raw_array, number_of_elements) }; slice diff --git a/mullvad-ios/src/api_client/shadowsocks_loader.rs b/mullvad-ios/src/api_client/shadowsocks_loader.rs index 2d63b179d350..8c716fbe5df7 100644 --- a/mullvad-ios/src/api_client/shadowsocks_loader.rs +++ b/mullvad-ios/src/api_client/shadowsocks_loader.rs @@ -47,6 +47,9 @@ unsafe impl Send for SwiftShadowsocksLoaderWrapperContext {} impl SwiftShadowsocksLoaderWrapperContext { pub fn get_bridges(&self) -> Option { + if self.shadowsocks_loader.is_null() { + return None; + } // SAFETY: See notice for `swift_get_shadowsocks_bridges` let raw_configuration = unsafe { swift_get_shadowsocks_bridges(self.shadowsocks_loader) }; if raw_configuration.is_null() { diff --git a/test/Cargo.lock b/test/Cargo.lock index 2e21f1b824d6..4d9dbaa6edc2 100644 --- a/test/Cargo.lock +++ b/test/Cargo.lock @@ -330,24 +330,6 @@ dependencies = [ "cipher", ] -[[package]] -name = "cbindgen" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadd868a2ce9ca38de7eeafdcec9c7065ef89b42b32f0839278d55f35c54d1ff" -dependencies = [ - "heck 0.4.1", - "indexmap", - "log", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn 2.0.100", - "tempfile", - "toml", -] - [[package]] name = "cc" version = "1.2.16" @@ -1981,7 +1963,6 @@ version = "0.0.0" dependencies = [ "anyhow", "async-trait", - "cbindgen", "chrono", "futures", "http", @@ -2008,7 +1989,6 @@ dependencies = [ "tokio-rustls", "tokio-socks", "tower", - "uuid", "vec1", ] @@ -3152,15 +3132,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_spanned" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" -dependencies = [ - "serde", -] - [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3933,40 +3904,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "toml" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.22.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - [[package]] name = "tonic" version = "0.13.1" @@ -4916,15 +4853,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "winnow" -version = "0.6.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e90edd2ac1aa278a5c4599b1d89cf03074b610800f866d4026dc199d7929a28" -dependencies = [ - "memchr", -] - [[package]] name = "winreg" version = "0.50.0"